GraphQL(四)- Query, Mutation, Subscription 在 ApolloGraph中的实现示例

本文讨论GraphQL中的Query,Mutation和Subscription,并给出ApolloGraph中的实现示例

Query和Mutation是GraphQL中的最基本操作,可以理解为:

  • query => GET
  • mutation => POST, PUT, PATCH, DELETE

在GraphQL中两者被这样区分开,分离了读写操作,对于读写权限这块有个非常好的分隔。
在下面的示例中,我们定义了这样一个type:

type Employee {
	name: String!
	age: Int!
	job: String!
	hobbies: [String]
}

Query

我们通过下面的例子,查询内容:
后端示例:

const schema = gql`
	type Employee {
		name: String!
		age: Int!
		job: String!
		hobbies: [String]
	}

	type Query {
		employees: [Employee]
	}
`

export const employeesDB = [
	{
		name: 'Rick',
		age: 35,
		job: 'Sale',
		hobbies: ['Swimming', 'Reading']
	},
	{
		name: 'Max',
		age: 27,
		job: 'Software engineer',
		hobbies: ['Video games']
	}
];

export const resolver = {
	Query: {
		employees: (parent: any, args: any, ctx: any, info: any) => {
			return employeesDB;
		},
	}
};

const schema = makeExecutableSchema({
  typeDefs: schema,
  resolvers: resolver
});

const app = express();
const httpServer = createServer(app);

// Set up ApolloServer.
const server = new ApolloServer({
  schema,
  plugins: [
    ApolloServerPluginDrainHttpServer({ httpServer }),
    ApolloServerPluginLandingPageLocalDefault({ embed: true }),
  ],
});

server.start().then(
  () => {
    server.applyMiddleware({ app });
    const PORT = 4000;
    // Now that our HTTP server is fully set up, we can listen to it.
    httpServer.listen(PORT, () => {
      console.log(`Server is now running on http://localhost:${PORT}${server.graphqlPath}`);
    });
  }
)

前端(Angular-Apollo):

graphql.module.ts

const PORT = 4000;
const uri = `http://localhost:${PORT}/graphql`;

export function createApollo(httpLink: HttpLink): ApolloClientOptions<any> {
  return {
    link: httpLink.create({uri}),
    cache: new InMemoryCache(),
  };
}

@NgModule({
  exports: [ApolloModule],
  providers: [
    {
      provide: APOLLO_OPTIONS,
      useFactory: createApollo,
      deps: [HttpLink],
    },
  ],
})
export class GraphQLModule {}

Employee Component

const employeesQuery = gql`
	query GetEmpoyeesAll {
		employees {
			name
			age
			job
			hobbies
		}
	}
`;

@Component({
	selector: 'app-employees-panel',
	templateUrl: './employees-panel.component.html',
	styleUrls: ['./employees-panel.component.scss'],
})
export class EmployeesPanelComponent implements OnInit {

	employees: any[] = [];
	loading = true;
	error: any;

	ngOnInit(): void {
		this.apollo
			.watchQuery({
			query: employeesQuery,
		})
			.valueChanges.subscribe((result: any) => {
			this.employees = result?.data?.employees;
			this.loading = result.loading;
			this.error = result.error;
		});
	}
}

Mutation

我们为上例添加一个方法,AddEmployee,定义对应类型和方法:

input AddEmployeeInput {
	name: String!
	age: Int!
	job: String!
	hobbies: [String]
}

type Mutation {
	addEmployee(data: AddEmployeeInput):Employee
}

后端:
需要在上例的基础上,添加AddEmployee的resolver:

export const resolver = {
	addEmployee: (parent: any, args: any, ctx: any, info: any) => {
		const newEmployee = parent.data;
		employeesDB.push(newEmployee);
		return newEmployee;
	}
};

前端:
在上例的基础上,前端增加一个添加按钮,绑定如下事件:

onCreateEmployee() {
	const createEmployeeMutation = gql`
		mutation {
			addEmployee(employee: {
				name: 'Lina',
				age: 44,
				job: 'manager',
				hobbies: ['Reading', 'Dancing'],
			}) {
				name
			}
		  }
    `
	this.apollo
		.mutate({mutation: createBookMutation})
		.subscribe((result: any) => {
		this.apollo
			.watchQuery({
			query: createEmployeeMutation,
		})
			.valueChanges.subscribe((result: any) => {
			this.employees = result?.data?.employees;
			this.loading = result.loading;
			this.error = result.error;
		});
	})
}

Subscription

Subscription 用于后端主动向前端发消息的情形,相较于Query和Mutation用得较少,通常用websocket添加Midware
在Apollo Graph中,需要引入第三方的library来实现,可以用graph-ws,向前端添加一个计数器示例如下:

安装graph-ws:

npm install graph-ws

gql中添加Subscription:

type Subscription {
	count: Int
}

resolver:

export const resolver = {
	Subscription: {
		count: {
			subscribe: () => {
				let count = 0;
				setInterval(() => {
				  count++
				  pubsub.publish('count', {
					count
				  })
				}, 1000)
				return pubsub.asyncIterator('count');
			}
		}
	}
}

后端引入Subscription,需要创建一个websocket实例作为Midware:

const schema = makeExecutableSchema({
	typeDefs: schema,
	resolvers: resolver
});

// Create an Express app and HTTP server; we will attach both the WebSocket
// server and the ApolloServer to this HTTP server.
const app = express();
const httpServer = createServer(app);

// Create our WebSocket server using the HTTP server we just set up.
const wsServer = new WebSocketServer({
	server: httpServer,
	path: '/graphql',
});
// Save the returned server's info so we can shutdown this server later
const serverCleanup = useServer({ schema }, wsServer);

// Set up ApolloServer.
const server = new ApolloServer({
	schema,
	csrfPrevention: true,
	cache: 'bounded',
	context: (req) => {
		const request = req.req;
		return {tags, tagGroups, users, request}
	},
	plugins: [
		// Proper shutdown for the HTTP server.
		ApolloServerPluginDrainHttpServer({ httpServer }),

		// Proper shutdown for the WebSocket server.
		{
			async serverWillStart() {
				return {
					async drainServer() {
						await serverCleanup.dispose();
					},
				};
			},
		},
		ApolloServerPluginLandingPageLocalDefault({ embed: true }),
	],
});
server.start().then(
	() => {
		server.applyMiddleware({ app });

		const PORT = 4000;
		// Now that our HTTP server is fully set up, we can listen to it.
		httpServer.listen(PORT, () => {
			console.log(`Server is now running on http://localhost:${PORT}${server.graphqlPath}`);
		});
	}
)
posted @ 2023-03-13 19:57  Asp1rant  阅读(285)  评论(0编辑  收藏  举报