apollo服务器使用教程_使用Apollo Server和AdonisJS构建GraphQL服务器

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 adonis-apollo-server which integrates Apollo server with the AdonisJS framework.

最近,我一直在探索GraphQL。 Apollo(客户端和服务器)确实使使用GraphQL变得很棒。 Apollo服务器开箱即用地支持某些NodeJS框架。 对于NodeJS框架,AdonisJS是我的首选。 不幸的是,Apollo服务器不支持开箱即用的AdonisJS。 因此,我创建了一个名为adonis-apollo-server的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服务器。

我们将要建设的 ( What We’ll be Building )

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服务器。 该博客将具有UserPost实体。 我们将能够创建用户并使用JSON Web令牌(JWT)对他们进行身份验证。 经过身份验证的用户将可以创建帖子。 因此,用户和帖子将具有一对多关系。 也就是说,一个用户可以有多个帖子,而一个帖子只能属于一个用户。

要求 ( Requirements )

This tutorial assumes you have the following installed on your computer:

本教程假定您在计算机上安装了以下软件:

  • Node.js 8.0 or greater

    Node.js 8.0或更高版本
  • Npm 3.0 or greater

    Npm 3.0或更高

创建一个新的AdonisJS应用 ( Create a new AdonisJS app )

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-server
adonis serve --dev

Then visit http://127.0.0.1:3333, you should get a JSON response as below:

然后访问http://127.0.0.1:3333 ,您应该获得如下的JSON响应:

{
      "greeting": "Hello world in JSON"
    }

安装依赖项 ( Installing Dependencies )

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.

让我们快速查看每个依赖项。

  • graphql: a reference implementation of GraphQL for JavaScript.

    graphql: GraphQL for JavaScript的参考实现。
  • adonis-apollo-server: apollo server for AdonisJS.

    adonis-apollo服务器:用于AdonisJS的apollo服务器。
  • graphql-tools: for building a GraphQL schema and resolvers in JavaScript.

    graphql-tools:用于在JavaScript中构建GraphQL模式和解析器。
  • slugify: slugify a string.

    slugify:对字符串进行slugify。

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.js

const providers = [
    ...
    'adonis-apollo-server/providers/ApolloServerProvider'
]

数据库设置 ( Database Setup )

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并将以下代码段添加到其中:

// .env

DB_CONNECTION=mysql
DB_HOST=localhost
DB_DATABASE=adonis_graphql_server
DB_USER=root
DB_PASSWORD=

Remember to update the database name, username and password accordingly with your own database settings.

切记使用您自己的数据库设置相应地更新数据库名称,用户名和密码。

定义GraphQL模式 ( Defining GraphQL Schema )

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 language
const 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.js

type 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.js

type 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.js

const resolvers = require('./resolvers')

Then we can build the schema:

然后我们可以构建模式:

// app/data/schema.js

// Type definition
...

module.exports = makeExecutableSchema({ typeDefs, resolvers })

创建模型和迁移 ( Creating Models and Migration )

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.js

up () {
  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

定义模型关系 ( Defining Models Relationship )

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')
}

编写解析器 ( Writing Resolvers )

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 resolvers
const 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 serializers. 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.js

Mutation: {
  // 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.js

User: {
  // 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.js

module.exports = resolvers

创建GraphQL服务器 ( Creating the GraphQL Server )

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 GETPOST请求提供服务,因此我们定义了一个/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。

测试服务器 ( Testing Out the Server )

I will be using Insomnia to test out the server. Insomnia is a REST client that has support for GraphQL query. You can download it if you don’t have it already.

我将使用Insomnia来测试服务器。 Insomnia是具有GraphQL查询支持的REST客户端。 如果尚未下载 ,可以下载

Make sure the server is already started:

确保服务器已启动:

adonis serve --dev

Which should be running on http://127.0.0.1:3333.

哪个应该在http://127.0.0.1:3333上运行。

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 http://127.0.0.1:3333/graphql in the address bar:

接下来,在地址栏中输入http://127.0.0.1:3333/graphql

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
        }
    }
}

结论 ( Conclusion )

There we have it! So, in this tutorial, we saw how to integrate Apollo server in an AdonisJS using the adonis-apollo-server 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.

到了! 因此,在本教程中,我们看到了如何使用adonis-apollo-server软件包将Apollo服务器集成到AdonisJS中。 然后,我们继续用它构建一个GraphQL服务器。 我们还看到了如何使用JSON Web令牌(JWT)向GraphQL服务器添加身份验证。 最后,我们看到了如何使用Insomnia REST客户端测试GraphQL服务器。

So, go forth and build awesome GraphQL apps with AdonisJS.

因此,继续使用AdonisJS构建很棒的GraphQL应用程序。

翻译自: https://scotch.io/tutorials/build-a-graphql-server-with-apollo-server-and-adonisjs

apollo服务器使用教程

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值