掘金 后端 ( ) • 2024-04-06 15:44

@apollo/server3与@apollo/server4采用了不同的方式: 3:express作为apolloserver的中间件 4:apolloserver提供了一个expressMiddleware的中间件,作为express的中间件运行 官网给了很详细的3-->4的说明[https://www.apollographql.com/docs/apollo-server/migration]

示例代码

import { ApolloServer } from "@apollo/server";
import { expressMiddleware } from "@apollo/server/express4";
import { ApolloServerPluginDrainHttpServer } from "@apollo/server/plugin/drainHttpServer";
import express from "express";
import http from "http";
import cors from "cors";
import { typeDefs, resolvers } from "./schema";
const app = express();
///////
const httpServer = http.createServer(app);
//////
const server = new ApolloServer({
  typeDefs,
  resolvers,
  plugins: [ApolloServerPluginDrainHttpServer({ httpServer })],
});
await server.start();
/////应用expressMiddleware中间件:将graphql ApolloServer服务集成express
app.use(
  "/graphql",
  cors(),
  express.json(),
  expressMiddleware(server, {
    context: async ({ req }) => ({ token: req.headers.token }),
  })
);
await new Promise((resolve) => httpServer.listen({ port: 4000 }, resolve));
console.log(`🚀 Server ready at http://localhost:4000/graphql`);

在大多数情况下,直接使用const app = express();然后通过app.listen(...)启动Express应用程序已经足够用了。这种方式对于绝大多数基于Express的Web应用来说都是充分的,因为app.listen(...)在内部会创建一个HTTP服务器。

然而,使用http.createServer(app)并结合const app = express();有其特定的用途和优势,特别是当您需要更细粒度的控制或想要集成其他类型的HTTP服务时。

为什么使用http.createServer(app)

  1. 更高级的服务器设置

    • 创建一个显式的HTTP服务器对象可以让您对服务器进行更详细的配置和控制。例如,您可能需要配置HTTPS服务器或与WebSocket服务器共享相同的HTTP服务器实例。
  2. 与其他HTTP服务共享

    • 在某些情况下,您可能需要让Express应用和其他HTTP服务(如WebSocket服务)共享同一个HTTP服务器。这在实现实时通信功能时很常见。
  3. 统一管理

    • 创建一个单独的HTTP服务器实例可以让您在一个地方管理HTTP相关的所有设置,这可能会让代码组织得更清晰,特别是在复杂的应用中。

示例

使用单独的HTTP服务器实例可以在WebSocket服务器和Express应用之间共享同一个HTTP/S服务器:

javascriptCopy code
const express = require('express');
const http = require('http');
const WebSocket = require('ws');

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

// 设置WebSocket服务器
const wss = new WebSocket.Server({ server: httpServer });

// Express路由
app.get('/', (req, res) => {
  res.send('Hello World');
});

// 启动服务器
httpServer.listen(3000, () => {
  console.log('Server is running on port 3000');
});

在这个例子中,Express应用和WebSocket服务都通过同一个httpServer实例运行,这意味着它们可以共享相同的端口和底层网络资源。

总结

虽然在许多简单应用中直接使用app.listen(...)就足够了,但在需要更高级配置或特定集成(如与WebSocket一起使用)的情况下,显式创建一个HTTP服务器实例并将Express应用作为中间件使用是一个更灵活的选择。

简化版示例

import cors from "cors";
import express from "express";
import { authMiddleware, handleLogin } from "./auth.js";
import { ApolloServer } from "@apollo/server";
import { expressMiddleware } from "@apollo/server/express4";
const PORT = 9000;
const app = express();
//照常做express的中间件控制
app.use(cors(), express.json(), authMiddleware);
//照常设置express 路由
app.post("/login", handleLogin);
// 添加一个ApolloServer 并启动
const apolloServer = new ApolloServer();
await apolloServer.start();
//应用expressMiddleware -->将apollo server集成进express server
app.use("/graphql", expressMiddleware(apolloServer));
//启动express服务
app.listen({ port: PORT }, () => {
  console.log(`Server running on port ${PORT}`);
});

选择"/graphql"这个路由来应用中间件,其实是一个非官方的标准或约定;

  • 区分不同的服务:如果您的应用同时提供GraphQL和RESTful服务,使用/graphql可以明确区分这两种服务的路由,减少混淆。
  • 中间件分离:在Express等框架中,您可以通过定义不同的路由来分别管理不同的中间件。这意味着所有发送到/graphql的请求都将专门由GraphQL中间件处理。

代码

image.png

test

image.png