从零到无服务器 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
选择“创建并配置新站点”选项,然后通过其他选项选择默认设置。
创建文件.gitignore
、netlify.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
,该对象与您的架构相对应。在本例中,这意味着名为 的键
下有一个函数。 架构要求将一个类型作为结果,因此这就是我们将从解析器返回的结果。Query
hello
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前端。
尼基·穆尔曼@nmeuleman
看到一个叫 Jason 的人提到 JSON,你是不是也觉得挺有意思的?
📣 JaSON API 隆重推出
jason-api.netlify.com
一个无服务器的 GraphQL API,全是 Jason 的玩意儿!
探索酷炫科技,再来个傻傻的笑话,就能搞定!2019年10月4日下午16:56
在这篇博文的下一部分中,我们将探讨如何使用多个文件来创建我们的 GraphQL 端点并用FaunaDB替换我们的数据库。
为抢先了解,请随意查看示例的代码。