从零到无服务器 GraphQL 端点

2025-06-10

从零到无服务器 GraphQL 端点

无服务器 GraphQL。

这两个词的组合能触发你的流行语探测器吗?
这一定很难,对吧?

我来告诉你,绝对不是!
我们将在这篇博文中创建一个无服务器的 GraphQL 端点,我保证,这比你想象的要简单。

准备

我们将使用Netlify来托管 GraphQL 端点。
它使网站托管变得快速便捷。它还提供了一种使用无服务器函数的方法,这正是我们真正感兴趣的。这些函数将部署在AWS Lambda上,但您无需担心如何实现,Netlify 会为您完成。

首先安装Netlify CLI 工具

npm install netlify-cli -g

为这个新的、很棒的项目创建一个新目录并做一些 忙碌的工作初始设置

git init
npm init -y

可选择在GitHub上创建一个存储库,以帮助 Netlify 在每次发布代码更新时自动部署。

netlify login
netlify init

选择“创建并配置新站点”选项,然后通过其他选项选择默认设置。

netlify init

创建文件.gitignorenetlify.toml文件和文件夹。.netlify

该文件netlify.toml保存了 Netlify 站点的配置。
您将在其中看到functions = "functions"一行。
这是将部署到AWS Lambda 的"functions"无服务器函数的路径。

创建一个index.html文件,以便您访问该网站时显示一些内容。

此时,您已经拥有一个正常运行的网站,让我们添加第一个无服务器功能。

第一个无服务器函数

创建一个functions文件夹(或任何你将该路径更改为的文件netlify.toml夹)。

.js这里的每个文件都是一个无服务器函数。
文件名也是无服务器函数的名称。
它们将在以下位置访问:/.netlify/functions/{function_name}

每个文件都应该导出一个handler函数。

exports.handler = function(event, context, callback) {
  // Hey, I'm lighter than a server, I'm server~~less~~ 🥁💥
};

使用callback参数返回错误或函数的响应。

在目录内functions创建一个hello.js文件。

exports.handler = (event, context, callback) => {
  const response = {
    statusCode: 200,
    body: JSON.stringify({
      msg: `Mr. Mime is the best Pokemon`
    })
  };
  callback(undefined, response);
};

你可以在这里选择另一个口袋妖怪,但你错了🤷‍♂

要查看其运行情况,请运行netlify dev

netlify dev

打开 URL 并转到/.netlify/functions/hello

🎉 TADA,有回应。

使用单个文件的无服务器 GraphQL

是时候创建另一个无服务器功能了,这也是本文的目的,即 GraphQL 端点!

我们将使用该apollo-server-lambda包来实现这一点。

npm install apollo-server-lambda

不要忘记将node_modules文件夹添加到.gitignore以防止将该包中的代码提交到 git。

在目录内functions创建一个graphql.js文件。

通过导入您需要的东西来启动该文件。

const { ApolloServer, gql } = require('apollo-server-lambda');

GraphQL 服务器的两大部分都存在于此文件中:
Schema(或 typeDefs)和解析器。

模式定义了你可以执行哪些操作,以及使用的数据是什么样子的。解析器定义如何执行这些操作的函数。

因此,这两个部分必须同步。如果不同步,Apollo 服务器就会发出警报,你的 GraphQL 端点将无法工作!

有关模式的更多信息,apollo 在其教程中有一个很棒的模式步骤,这也是我们在这里所做的,只是这一次,我们将使用无服务器,而那些文档使用“老式”服务器。

const typeDefs = gql`
  type Query {
    hello: String!
  }
`;

完成最小模式后,就该转到解析器了!

解析器是具有以下签名的函数

fieldName(obj, args, context, info) { result }

有关解析器的更多信息,apollo 文档中有大量信息。

在该graphql.js文件中,定义一个名为 的对象resolvers,该对象与您的架构相对应。在本例中,这意味着名为 的键

下有一个函数 架构要求将一个类型作为结果,因此这就是我们将从解析器返回的结果。Queryhello
string

const resolvers = {
  Query: {
    hello: (obj, args, context) => {
      return 'Hello, world!';
    }
  }
};

太棒了!该文件现在包含两个变量,但它们尚未使用。

现在,您可以将刚刚创建的变量添加到文件顶部ApolloServer导入的文件中。apollo-server-lambda

为了说明目的,添加playground: true, introspection: true打开它并确保您在访问时看到一些有用的内容/.netlify/functions/graphql

const server = new ApolloServer({
  typeDefs,
  resolvers,
  playground: true,
  introspection: true
});

快完成了。还记得我们的无服务器函数应该如何导出吗handler

值得庆幸的是,有一种便捷的方法server可以为您完成此操作。

exports.handler = server.createHandler();

准备起飞🚀。

运行netlify dev并访问以使用该 URL 上的GraphQL 游乐场/.netlify/functions/graphql与您刚刚创建的无服务器 GraphQL 端点进行交互

你好查询

扩展 GraphQL 端点

只能查询hello并且查询总是返回相同的字符串很无聊,让我们添加一些口袋妖怪。

架构

模式现在看起来像这样

const typeDefs = gql`
  type Query {
    hello: String!
    allPokemon: [Pokemon]!
    pokemonById(id: Int!): Pokemon
    pokemonByName(name: String!): Pokemon
  }
  type Mutation {
    createPokemon(id: Int!, name: String!, isVeryBest: Boolean!): Pokemon
    deletePokemon(id: Int!): Pokemon
    updatePokemon(id: Int!, name: String, isVeryBest: Boolean): Pokemon
  }
  type Pokemon {
    id: ID!
    name: String!
    isVeryBest: Boolean!
  }
`;

请注意,我们不仅定义了可以在下请求的字段Query,还定义了表示我们可以在Mutation键下采取的操作的字段以及单个数据的格式Pokemon

我会参考Apollo Schema 文档来了解更多信息。如需更详细的信息,请参阅官方 GraphQL Schema 文档

数据库

为了支持我们想要做的事情,需要一个数据库!

为了简单起见,我们将在文件中添加一个 JavaScript 数组。

// I know the plural is Pokemon, don't judge me
const pokemons = [
  { id: 122, name: 'Mr. Mime', isVeryBest: true },
  { id: 25, name: 'Pikachu', isVeryBest: false },
  { id: 7, name: 'Squirtle', isVeryBest: false }
];

解析器

我们的解析器应该与我们的模式匹配,因此文件的该部分现在看起来像这样:

const resolvers = {
  Query: {
    hello: (obj, args, context) => {
      return 'Hello, world!';
    },
    allPokemon: (obj, args, context) => {
      return pokemons;
    },
    pokemonById: (obj, args, context) => {
      return pokemons.find(pokemon => pokemon.id === args.id);
    },
    pokemonByName: (obj, args, context) => {
      return pokemons.find(pokemon => pokemon.name === args.name);
    }
  },
  Mutation: {
    createPokemon: (obj, args, context) => {
      const pokemon = {
        id: args.id,
        name: args.name,
        isVeryBest: args.isVeryBest
      };
      pokemons.push(pokemon);
      return pokemon;
    },
    updatePokemon: (obj, args, context) => {
      const pokemon = pokemons.find(pokemon => pokemon.id === args.id);
      if (args.name) pokemon.name = args.name;
      if (args.isVeryBest) pokemon.isVeryBest = args.isVeryBest;
      return pokemon;
    },
    deletePokemon: (obj, args, context) => {
      const index = pokemons.findIndex(pokemon => pokemon.id === args.id);
      const pokemon = pokemons[index];
      pokemons.splice(index, 1);
      return pokemon;
    }
  }
};

尝试添加

再次运行netlify dev并访问 GraphQL 游乐场,您可以与更加充实的 GraphQL 端点进行交互。

创造口袋妖怪

请记住,由于该超高科技数据库是一个 JavaScript 数组,因此当无服务器功能关闭并重新启动时,对其所做的任何更改都将消失!

结论

这确实让我很兴奋,所以我把它用在一个基本上是一个失控笑话的副项目中。

它有一个无服务器的 GraphQL 端点,可以与FaunaDB对话,用TypeScript编写,并有一个小型的Gatsby前端。

在这篇博文的下一部分中,我们将探讨如何使用多个文件来创建我们的 GraphQL 端点并用FaunaDB替换我们的数据库。

为抢先了解,请随意查看示例的代码。

GitHub 徽标 NickyMeuleman / serverless-graphql

无服务器 GraphQL 端点

鏂囩珷鏉ユ簮锛�https://dev.to/nickymeuleman/from-zero-to-a-serverless-graphql-endpoint-in-a-flash-2lho
PREV
新的 Windows 终端
NEXT
教程:使用 Mux 和 Stream Chat 进行实时流式传输