本文共 18772 字,大约阅读时间需要 62 分钟。
apollo服务器使用教程
Recently, I have been exploring GraphQL. Apollo (client and server) has really made working with GraphQL awesome. Apollo server has support for some NodeJS frameworks out of the box. When it is comes to NodeJS frameworks, AdonisJS is my preferred choice. Unfortunately, Apollo server does not support AdonisJS out of the box. For this reason, I created an AdonisJS package called which integrates Apollo server with the AdonisJS framework.
最近,我一直在探索GraphQL。 Apollo(客户端和服务器)确实使使用GraphQL变得很棒。 Apollo服务器开箱即用地支持某些NodeJS框架。 对于NodeJS框架,AdonisJS是我的首选。 不幸的是,Apollo服务器不支持开箱即用的AdonisJS。 因此,我创建了一个名为的AdonisJS软件包,该软件包将Apollo服务器与AdonisJS框架集成在一起。
In this tutorial, I will show you how to build a GraphQL server with Apollo server and AdonisJS using the package above.
在本教程中,我将向您展示如何使用上述软件包使用Apollo服务器和AdonisJS构建GraphQL服务器。
This tutorial assumes you have some basic understanding of GraphQL. With that said, let's jump in and start building our GraphQL server.
本教程假定您对GraphQL有一些基本的了解。 话虽如此,让我们开始构建GraphQL服务器。
For the purpose of this tutorial, we’ll build a GraphQL server with the concept of a basic blog. The blog will have User and Post entities. We’ll be able to create users and authenticate them using JSON Web Tokens (JWT). Authenticated users will be able to create posts. Hence, User and Post will have a one-to-many relationship. That is, a user can have many posts, while a post can only belong to a user.
就本教程而言,我们将使用基本博客的概念来构建GraphQL服务器。 该博客将具有User和Post实体。 我们将能够创建用户并使用JSON Web令牌(JWT)对他们进行身份验证。 经过身份验证的用户将可以创建帖子。 因此,用户和帖子将具有一对多关系。 也就是说,一个用户可以有多个帖子,而一个帖子只能属于一个用户。
This tutorial assumes you have the following installed on your computer:
本教程假定您在计算机上安装了以下软件:
We’ll start by creating a new AdonisJS app. We’ll use the adonis
CLI for this, so run the command below if you don’t have it installed already:
我们将从创建一个新的AdonisJS应用开始。 我们将为此使用adonis
CLI,因此,如果尚未安装,请运行以下命令:
npm i -g @adonisjs/cli
With that installed, let’s create a new AdonisJS app. We'll call it adonis-graphql-server
:
安装完成后,让我们创建一个新的AdonisJS应用。 我们将其称为adonis-graphql-server
:
adonis new adonis-graphql-server --api-only
We are specify that we want the API only app by passing api-only
flag. This will create an app well suited for building APIs as things like views won’t be included.
我们通过传递api-only
标志来指定我们希望仅API应用。 这将创建一个非常适合构建API的应用程序,因为将不包括视图之类的东西。
We can start the app to make sure everything is working as expected:
我们可以启动该应用程序以确保一切正常。
cd adonis-graphql-serveradonis serve --dev
Then visit , you should get a JSON response as below:
然后访问 ,您应该获得如下的JSON响应:
{ "greeting": "Hello world in JSON" }
Next, we need to install the dependencies needed for our GraphQL server.
接下来,我们需要安装GraphQL服务器所需的依赖项。
npm install graphql adonis-apollo-server graphql-tools slugify --save
Let’s quickly go each of the dependencies.
让我们快速查看每个依赖项。
Before we can start using the adonis-apollo-server
package, we need to first register the provider inside start/app.js
:
在开始使用adonis-apollo-server
软件包之前,我们需要首先在start/app.js
注册提供程序:
// start/app.jsconst providers = [ ... 'adonis-apollo-server/providers/ApolloServerProvider']
We’ll be using MySQL
in this tutorial. So, we need to install Node.js driver for MySQL:
在本教程中,我们将使用MySQL
。 因此,我们需要为MySQL安装Node.js驱动程序:
npm install mysql --save
Once that’s installed, we need to make AdonisJS know we are using MySQL. Open .env
and add the snippet below to it:
安装完成后,我们需要让AdonisJS知道我们正在使用MySQL。 打开.env
并将以下代码段添加到其中:
// .envDB_CONNECTION=mysqlDB_HOST=localhostDB_DATABASE=adonis_graphql_serverDB_USER=rootDB_PASSWORD=
Remember to update the database name, username and password accordingly with your own database settings.
切记使用您自己的数据库设置相应地更新数据库名称,用户名和密码。
GraphQL schema describe how data are shaped and what data on the server can be queried or modified. Schema can be of two types: Query and Mutation. As described in the “What We’ll be Building” section, we’ll define schema for User and Post types. To do this, create a new directory named data
within the app
directory. Within the data
directory, create a new schema.js
file and paste the code below in it:
GraphQL模式描述了数据的成形方式以及可以查询或修改服务器上的哪些数据。 模式可以有两种类型:查询和突变。 如“我们将要构建的内容”部分所述,我们将为用户和帖子类型定义架构。 为此,请在app
目录中创建一个名为data
的新目录。 在data
目录中,创建一个新的schema.js
文件,并将以下代码粘贴到其中:
// app/data/schema.js'use strict'const { makeExecutableSchema } = require('graphql-tools')// Define our schema using the GraphQL schema languageconst typeDefs = ` type User { id: Int! username: String! email: String! posts: [Post] } type Post { id: Int! title: String! slug: String! content: String! user: User! }`
The fields are pretty straightforward. The User type also has a posts
field which will be an array of all posts created by a user. Similarly, the Post type also has a user
field which will be the user that created a post. You’ll noticed we attached !
while defining the user
field. This simply means, the user
field can not be NULL
or empty. That is to say, a post must be created by a user.
这些字段非常简单。 用户类型还具有一个posts
字段,该字段将是用户创建的所有帖子的数组。 同样,“帖子”类型也有一个“ user
字段,它将是创建帖子的用户。 您会注意到我们很重视!
同时定义user
字段。 这仅表示user
字段不能为NULL
或为空。 也就是说,帖子必须由用户创建。
Having defined our types, let’s now the define the queries we want to be carried out on them. Still within app/data/schema.js
, paste the code below just after the Post
type:
定义了类型之后,现在让我们定义要对它们执行的查询。 仍在app/data/schema.js
,将代码粘贴在Post
类型之后:
// app/data/schema.jstype Query { allUsers: [User] fetchUser(id: Int!): User allPosts: [Post] fetchPost(id: Int!): Post}
We are saying we want to be able to fetch all users and posts created and return them as an array. Also, fetch individual user and post by their ID respectively.
我们是说我们希望能够获取所有创建的用户和帖子,并将它们作为数组返回。 另外,分别获取单个用户并按其ID发布。
Next, we define some mutations. Mutation allows data to be modified on the server. Still within app/data/schema.js
, paste the code below just after Query
:
接下来,我们定义一些突变。 突变允许在服务器上修改数据。 仍然在app/data/schema.js
,将代码粘贴到Query
之后:
// app/data/schema.jstype Mutation { login (email: String!, password: String!): String createUser (username: String!, email: String!, password: String!): User addPost (title: String!, content: String!): Post}
The login
mutation will allow users to login into the server. It returns a string which will be a JWT. createUser
as it name suggests simply creates a new user. addPost
allows an authenticated user to create a post.
login
突变将允许用户登录到服务器。 它返回一个将是JWT的字符串。 顾名思义, createUser
只是创建一个新用户。 addPost
允许经过身份验证的用户创建帖子。
Finally, we need to build the schema. Before we do that, let’s add the resolvers (which we’ll create shortly) just after importing graphql-tools
:
最后,我们需要构建模式。 在此之前,让我们在导入graphql-tools
之后添加解析器(稍后将创建):
// app/data/schema.jsconst resolvers = require('./resolvers')
Then we can build the schema:
然后我们可以构建模式:
// app/data/schema.js// Type definition...module.exports = makeExecutableSchema({ typeDefs, resolvers })
Before we move on to writing resolvers, let’s take a moment to define and our models. We want our models to be as similar as possible to our schema. So, we’ll define User and Post models.
在继续编写解析器之前,让我们花点时间定义模型。 我们希望我们的模型与我们的架构尽可能相似。 因此,我们将定义用户和发布模型。
Luckily for us, AdonisJS ships with it a User model and migration file. We just need to create a Post model and migration file.
对我们来说幸运的是,AdonisJS附带了一个用户模型和迁移文件。 我们只需要创建一个Post模型和迁移文件。
adonis make:model Post -m
This will create a app/Models/Post.js
model and generate a migration file. Open the migration file database/migrations/xxxxxxxxxxxxx_post_schema.js
and update the up
method as below:
这将创建一个app/Models/Post.js
模型并生成一个迁移文件。 打开迁移文件database/migrations/xxxxxxxxxxxxx_post_schema.js
并更新up
方法,如下所示:
// database/migrations/xxxxxxxxxxxxx_post_schema.jsup () { this.create('posts', (table) => { table.increments() table.integer('user_id').unsigned().notNullable() table.string('title').notNullable() table.string('slug').unique().notNullable() table.text('content').notNullable() table.timestamps() })}
We can now run the migrations:
现在,我们可以运行迁移:
adonis migration:run
Recall, we said User and Post will have a one-to-many relationship. We’ll now define the relationship. Open app/Models/User.js
and add the code below within the User
class just after the tokens
method:
回想一下,我们说过User和Post将具有一对多关系。 现在,我们将定义关系。 打开app/Models/User.js
并在tokens
方法之后将以下代码添加到User
类中:
// app/Models/user.js/** * A user can have many posts. * * @method posts * * @return {Object} */posts () { return this.hasMany('App/Models/Post')}
Also, let's define the inverse relationship. Open app/Models/Post.js
and add the code below within the Post
class. This will be the only method the Post model will have:
另外,让我们定义逆关系。 打开app/Models/Post.js
然后在Post
类中添加以下代码。 这将是Post模型具有的唯一方法:
// app/Models/Post.js/** * A post belongs to a user. * * @method user * * @return {Object} */user () { return this.belongsTo('App/Models/User')}
With our models and their relationship defined, we can now move on to writing the resolvers. A resolver is a function that defines how a field in a schema is executed. Within app/data
directory, create a new resolvers.js
file and paste the code below it it:
定义好模型及其关系之后,我们现在可以继续编写解析器了。 解析器是定义模式中字段如何执行的功能。 在app/data
目录中,创建一个新的resolvers.js
文件,并将代码粘贴在其下面:
// app/data/resolvers.js'use strict'const User = use('App/Models/User')const Post = use('App/Models/Post')const slugify = require('slugify')// Define resolversconst resolvers = { Query: { // Fetch all users async allUsers() { const users = await User.all() return users.toJSON() }, // Get a user by its ID async fetchUser(_, { id }) { const user = await User.find(id) return user.toJSON() }, // Fetch all posts async allPosts() { const posts = await Post.all() return posts.toJSON() }, // Get a post by its ID async fetchPost(_, { id }) { const post = await Post.find(id) return post.toJSON() } },}
Firstly, we imported our models and the slugify package. Then we start by writing functions for retrieve our queries. Lucid (AdonisJS ORM) makes use of . So, whenever we query the database using Lucid models, the return value is always a serializer instance. Hence, the need for calling toJSON()
so as to return a formatted output.
首先,我们导入了模型和slugify包。 然后,我们开始编写用于检索查询的函数。 Lucid(AdonisJS ORM)使用 。 因此,每当我们使用Lucid模型查询数据库时,返回值始终是一个序列化器实例。 因此,需要调用toJSON()
以返回格式化的输出。
Next, we define functions for our mutations. Still within app/data/resolvers.js
, paste the code below just after Query
object:
接下来,我们为突变定义函数。 仍在app/data/resolvers.js
,将代码粘贴到Query
对象之后:
// app/data/resolvers.jsMutation: { // Handles user login async login(_, { email, password }, { auth }) { const { token } = await auth.attempt(email, password) return token }, // Create new user async createUser(_, { username, email, password }) { return await User.create({ username, email, password }) }, // Add a new post async addPost(_, { title, content }, { auth }) { try { // Check if user is logged in await auth.check() // Get the authenticated user const user = await auth.getUser() // Add new post return await Post.create({ user_id: user.id, title, slug: slugify(title, { lower: true }), content }) } catch (error) { // Throw error if user is not authenticated throw new Error('Missing or invalid jwt token') } }},
Because we created an api-only
app, the app is already configured to use of JWT for authentication.
由于我们创建了api-only
应用程序,因此该应用程序已配置为使用JWT进行身份验证。
Note: We need to set the
Authorization = Bearer <token>
header to authenticate the request.注意:我们需要设置
Authorization = Bearer <token>
标头来验证请求。
The login
function makes use of the auth
object which will be pass to the context object of GraphQL option when starting the server (more on this later). It then attempt to log the user in. If it was successful, a token (JWT) will be returned. The createUser
function simply creates a new user in the database. Lastly, the addPost
function, just like login()
, also accepts the auth
object as it third argument. It checks if the user is logged in, then get the details of the authenticated user and finally add a new post to the database. If the user is not logged in (that is, token was not found or is invalid) we return an error message.
login
功能利用了auth
对象,它将在启动服务器时传递给GraphQL选项的上下文对象(稍后将对此进行详细介绍)。 然后,它尝试登录用户。如果成功,将返回一个令牌(JWT)。 createUser
函数仅在数据库中创建一个新用户。 最后, addPost
函数就像login()
,也将auth
对象作为它的第三个参数。 它检查用户是否已登录,然后获取经过身份验证的用户的详细信息,最后向数据库中添加新帖子。 如果用户未登录(即未找到令牌或无效令牌),我们将返回错误消息。
Next, we define functions to retrieve the fields on our User, Post types respectively. Still within app/data/resolvers.js
, paste the code below just after Mutation
object:
接下来,我们定义函数以分别检索用户类型,帖子类型的字段。 仍在app/data/resolvers.js
,将代码粘贴到Mutation
对象之后:
// app/data/resolvers.jsUser: { // Fetch all posts created by a user async posts(userInJson) { // Convert JSON to model instance const user = new User() user.newUp(userInJson) const posts = await user.posts().fetch() return posts.toJSON() }},Post: { // Fetch the author of a particular post async user(postInJson) { // Convert JSON to model instance const post = new Post() post.newUp(postInJson) const user = await post.user().fetch() return user.toJSON() }}
Because we called toJSON()
on our queries above, for us to be able to call a relationship or any other method on the models, we need to first convert the JSON back to an instance of the model. Then we can call our relationship methods (posts()
and user()
) defined earlier.
由于我们在上面的查询中调用了toJSON()
,因此为了能够在模型上调用关系或任何其他方法,我们需要首先将JSON转换回模型的实例。 然后,我们可以调用前面定义的关系方法( posts()
和user()
)。
Finally, we export the resolvers:
最后,我们导出解析器:
// app/data/resolvers.jsmodule.exports = resolvers
We have successful build out each piece of our GraphQL server, let’s now put everything together. Open start/routes.js
and update it as below:
我们已经成功构建了GraphQL服务器的每一部分,现在让我们将所有内容放在一起。 打开start/routes.js
并进行如下更新:
// start/routes.js'use strict'const Route = use('Route')const GraphqlAdonis = use('ApolloServer')const schema = require('../app/data/schema');Route.route('/graphql', ({ request, auth, response }) => { return GraphqlAdonis.graphql({ schema, context: { auth } }, request, response)}, ['GET', 'POST'])Route.get('/graphiql', ({ request, response }) => { return GraphqlAdonis.graphiql({ endpointURL: '/graphql' }, request, response)})
Firstly, we imported Route
which we’ll use to define our routes. Then GraphqlAdonis
(the adonis-apollo-server package) and lastly we imported our schema.
首先,我们导入了Route
,我们将使用它来定义我们的路线。 然后是GraphqlAdonis
(adonis-apollo-server软件包),最后我们导入了架构。
Since GraphQL can be served over HTTP GET
and POST
requests, we defined a /graphql
route that accept both requests. Within this route, we call GraphqlAdonis’s graphql()
passing to it an object containing our schema and auth object (as the context) as GraphQL options. The graphql()
accepts 3 arguments. The first argument is the GraphQL options which is is an object. The remaining arguments are the request and response object respectively. We pass to the schema and auth object as the GraphQL options.
由于GraphQL可以通过HTTP GET
和POST
请求提供服务,因此我们定义了一个/graphql
路由,它接受两个请求。 在此路由中,我们调用GraphqlAdonis的graphql()
传递给它一个对象,该对象包含我们的模式和auth对象(作为上下文)作为GraphQL选项。 graphql()
接受3个参数。 第一个参数是GraphQL选项,它是一个对象。 其余参数分别是请求和响应对象。 我们将其作为GraphQL选项传递给架构和身份验证对象。
The /graphiql
route is solely for testing out the GraphQL server. Though I won’t be using it for testing out the server in this tutorial. I only added it just to show you how to use GraphiQL with your server.
/graphiql
路由仅用于测试GraphQL服务器。 尽管在本教程中我不会将其用于测试服务器。 我仅添加了它只是为了向您展示如何在服务器上使用GraphiQL。
I will be using to test out the server. Insomnia is a REST client that has support for GraphQL query. You can it if you don’t have it already.
我将使用来测试服务器。 Insomnia是具有GraphQL查询支持的REST客户端。 如果尚未 ,可以 。
Make sure the server is already started:
确保服务器已启动:
adonis serve --dev
Which should be running on .
哪个应该在上运行。
Start the Insomnia app:
启动失眠应用程序:
Click on create New Request. Give the request a name if you want, then select POST as the request method, then select GraphQL Query. Finally, click Create.
单击创建新请求 。 根据需要为请求命名,然后选择POST作为请求方法,然后选择GraphQL Query 。 最后,点击创建 。
Next, enter in the address bar:
接下来,在地址栏中输入 :
Try creating a new user with createUser
mutation:
尝试使用createUser
突变创建一个新用户:
mutation{ createUser(username: "mezie", email: "mezie@example.com", password: "password") { id username email }}
You should get a response as in the image below:
您应该得到如下图所示的响应:
Then login:
然后登录:
mutation{ login(email: "mezie@example.com", password: "password")}
A JWT is returned on successful login.
成功登录后将返回JWT。
To test out adding a new posting, remember we said only authenticated users can add post? Though we are successfully logged in, but we need to find a way to add the JWT generated above to the request headers. Luckily for us, this is pretty simple with Insomnia (the major reason I chose to test out the GraphQL server with it).
要测试添加新帖子,还记得我们说过只有经过身份验证的用户才能添加帖子吗? 尽管我们已经成功登录,但是我们需要找到一种方法将上面生成的JWT添加到请求标头中。 幸运的是,对于Insomnia来说,这非常简单(我选择使用它测试GraphQL服务器的主要原因)。
From Auth dropdown, select Bearer Token and paste the token (JWT) above in the field provider.
从“ 身份验证”下拉列表中,选择“ 承载令牌”并将上面的令牌(JWT)粘贴到字段提供程序中。
With that added, you can now add a new post:
添加后,您现在可以添加新帖子:
mutation{ addPost(title: "Hello Adonis", content: "Adonis is awesome!") { id title slug content user { username email } }}
There we have it! So, in this tutorial, we saw how to integrate Apollo server in an AdonisJS using the package. We then went on to build a GraphQL server with it. We also saw how to add authentication to the GraphQL server using JSON Web Tokens (JWT). Finally, we saw how to test our GraphQL server using the Insomnia REST client.
到了! 因此,在本教程中,我们看到了如何使用软件包将Apollo服务器集成到AdonisJS中。 然后,我们继续用它构建一个GraphQL服务器。 我们还看到了如何使用JSON Web令牌(JWT)向GraphQL服务器添加身份验证。 最后,我们看到了如何使用Insomnia REST客户端测试GraphQL服务器。
So, go forth and build awesome GraphQL apps with AdonisJS.
因此,继续使用AdonisJS构建很棒的GraphQL应用程序。
翻译自:
apollo服务器使用教程
转载地址:http://beuwd.baihongyu.com/