用Envelop配置任何GraphQL服务器

269 阅读5分钟

Envelop是一个轻量级的GraphQL插件库,允许你向GraphQL执行层添加自定义功能,而不管你使用的GraphQL服务器是什么。

Apollo GraphQLGraphite这样的GraphQL服务器已经有了针对平台的插件系统,并且运行良好。缺点是,如果你出于任何原因选择转移到不同的GraphQL实现,你将需要修改插件以与该特定实现一起工作。

什么是Envelop?

这就是Envelop的作用。Envelop不是一个GraphQL服务器--它是GraphQL的一个封装器,允许开发者编写与平台无关的插件。这意味着你只需要写一次插件,就可以让它与Envelop一起工作,并集成到任何GraphQL服务器。

为了学习如何实现这一点,我们将把Envelop与Fastify和Apollo GraphQL整合起来。

以下是你跟随本教程所需的内容。

设置Node应用程序

运行以下命令来安装我们需要的所有依赖项。

npm install  @envelop/core fastify graphql graphql-helix --save

接下来,我们需要创建一个服务器。创建一个server.js 文件并添加以下代码。

const { envelop, useSchema, useLogger } = require('@envelop/core');
const fastify = require('fastify');
const { processRequest, getGraphQLParameters } = require('graphql-helix');

在上面的代码中,我们正在导入envelop 和它的插件。如果你使用过React Hooks,插件的命名规则你会很熟悉。Envelop插件是用前缀和use 关键字命名的。在这个例子中,我们将同时添加useSchemauseLogger 插件。

对于初学者,我们将使用GraphQL Helix来构建我们的GraphQL服务器,以捕捉和处理对Fastify服务器的请求。

创建一个GraphQL模式

接下来,我们将创建一个schema.js 文件,在这里我们将定义我们的模式类型、突变和解析器,以便我们可以创建一个歌曲库。这个文件应该有以下内容。

const {
 GraphQLObjectType,
 GraphQLSchema,
 GraphQLString,
} = require("graphql");
const schema = new GraphQLSchema({
 mutation: new GraphQLObjectType({
   name: "Mutation",
   fields: () => ({
     echo: {
       args: {
         text: {
           type: GraphQLString,
         },
       },
       type: GraphQLString,
       resolve: (_root, args) => {
         return args.text;
       },
     },
   }),
 }),
 query: new GraphQLObjectType({
   name: "Query",
   fields: () => ({
     song: {
       type: new GraphQLObjectType({
         name: "Song",
         fields: () => ({
           firstVerse: {
             type: GraphQLString,
             resolve: () => "Singing me a song is lovely.",
           },
           secondVerse: {
             type: GraphQLString,
             resolve: () =>
               new Promise((resolve) =>
                 setTimeout(
                   () => resolve("You never wanted to sing with me?"),
                   5000
                 )
               ),
           },
         }),
       }),
       resolve: () => ({}),
     },
   }),
 }),
});

module.exports = schema

创建一个Fastify服务器

我们将导入刚刚创建的模式,并在server.js 中的Helix库导入后使用它,如下图所示。

const { envelop, useSchema, useLogger } = require('@envelop/core');
const fastify = require('fastify');
const { processRequest, getGraphQLParameters } = require('graphql-helix');
const mySchema = require("./mySchema"); //here

将Envelop与我们的模式进行整合

无论你使用什么GraphQL服务器和集成过程,下一步都相当简单。在我们的例子中,我们正在使用两个插件。 [useSchema()](https://github.com/dotansimha/envelop/blob/main/packages/core/src/plugins/use-schema.ts)[useLogger()](https://github.com/dotansimha/envelop/blob/main/packages/core/src/plugins/use-logger.ts).你可以使用任何你想要的插件 - 你只需要将插件添加到plugins 数组中,像这样。

const getEnveloped = envelop({
 plugins: [useSchema(mySchema), useLogger()],
});

这是集成的一个关键部分,因为它给你提供了一个高度的抽象,并允许你将所需的组件脱钩使用。

接下来,我们需要创建一个Fastify应用程序并集成Envelop。

const fastifyApp = fastify();
const port = 3000;

fastifyApp.route({
 method: ['POST'],
 url: '/graphql',
 async handler(req, res) {
   const { parse, validate, contextFactory, execute, schema } = getEnveloped({
     req,
   });
   const request = {
     body: req.body,
     headers: req.headers,
     method: req.method,
     query: req.query,
   };
   const { operationName, query, variables } = getGraphQLParameters(request);
   const result = await processRequest({
     operationName,
     query,
     variables,
     request,
     schema,
     parse,
     validate,
     execute,
     contextFactory,
   });
   if (result.type === 'RESPONSE') {
     res.status(result.status);
     res.send(result.payload);
   } else {
     res.send({ errors: [{ message: 'Not Supported' }] });
   }
 },
});
fastifyApp.listen(port, () => {
 console.log(`GraphQL server is running on port:`, port);
});

让我们来分解一下这段代码。我们首先设置了服务器并配置了GraphQL端点。然后我们从Envelop实例中导入了parsecontextFactoryexecuteschema ,如下图所示。

const { parse, validate, contextFactory, execute, schema } = getEnveloped({req});

这些是Envelop中的一些参数,我们将把它们传递给Helix来处理我们的GraphQL请求。

 const result = await processRequest({
     operationName,
     query,
     variables,
     request,
     schema,
     parse,
     validate,
     execute,
     contextFactory,
   });

现在我们可以用node server.js 命令来运行API,启动Node服务器。

用Postman测试应用程序

我们应该用Postman来测试我们的应用程序。

Postman GraphQL Request Page Testing Our App

Postman GraphQL请求的屏幕截图。

这就是了!我们已经成功地集成了Envelop,并为Node应用程序添加了一个自定义插件。

集成Envelop和Apollo GraphQL

就像我之前提到的,你可以将Envelop与任何GraphQL服务器集成。让我们来看看使用Apollo GraphQL的实现。

在一个单独的目录中,通过运行下面的命令安装GraphQL、Apollo和GraphQL工具

npm install @apollo/client graphql @graphql-tools/schema 

创建一个单独的JavaScript文件,名为app.js ,并在其中添加以下代码。

import { ApolloServer } from 'apollo-server';
import { envelop, useSchema, useLogger } from '@envelop/core';
import { makeExecutableSchema } from '@graphql-tools/schema';

const schema = makeExecutableSchema({
  typeDefs: /* GraphQL */ `
    type Query {
      hello: String!
    }
  `,
  resolvers: {
    Query: {
      hello: () => 'World',
    },
  },
});

const getEnveloped = envelop({
  plugins: [useSchema(schema), useLogger()],
});

const server = new ApolloServer({
  executor: async requestContext => {
    const { schema, execute, contextFactory } = getEnveloped({ req: requestContext.request.http });

    return execute({
      schema: schema,
      document: requestContext.document,
      contextValue: await contextFactory(),
      variableValues: requestContext.request.variables,
      operationName: requestContext.operationName,
    });
  },
});

server.listen(3000);

在上面的片段中,我们用GraphQL工具创建了一个可执行模式,用Envelop创建了一个实例,并添加了useSchema()useLogger() 插件。

const getEnveloped = envelop({
  plugins: [useSchema(schema), useLogger()],
});

请注意,useSchemauseLogger 都是与Envelop一起预先打包的。

最后,在Apollo服务器对象中,用Envelop对象的参数覆盖执行者对象。这就是你需要将Envelop与你的Apollo GraphQL服务器整合的全部内容。

总结

如果你用GraphQL构建应用程序,你会发现Envelop是你项目的一个重要补充。如果你喜欢保持你的代码干燥,这一点尤其正确,因为Envelop允许你编写模块化的GraphQL插件,可以在所有的GraphQL服务器实现上工作。谢谢你的阅读!

使用Envelop配置任何GraphQL服务器的文章首次出现在LogRocket博客上。