节点服务器和被管服务器
什么是GraphQL (What is GraphQL)
GraphQL is a Query Language and a server-side runtime that is used to request data from the server. The first thing that comes to the mind from the word “Query Language” is SQL. Just like SQL is used for querying databases, GraphQL is a bit like SQL but for querying Web API’s. It eliminates the need to repeatedly develop or change existing end-points. GraphQL also enables the client/front-end to retrieve exactly the data they have requested and no more. Which means, within a single request of GraphQL, you can traverse from entry point to the related data. Whereas, in RESTful API you have to call multiple endpoints to fetch similar results.
GraphQL是一种查询语言,是一种服务器端运行时,用于从服务器请求数据。 从“查询语言”一词想到的第一件事就是SQL。 就像SQL用于查询数据库一样,GraphQL有点像SQL,但是用于查询Web API。 它消除了重复开发或更改现有端点的需要。 GraphQL还使客户端/前端可以准确地检索他们已请求的数据,而不再需要其他数据。 这意味着,在GraphQL的单个请求中,您可以从入口点遍历到相关数据。 而在RESTful API中,您必须调用多个端点以获取相似的结果。
The following example will help you to understand it better
以下示例将帮助您更好地理解它
Let us consider an object Person which has the attributes name, age, email, contactNumber. Suppose the front-end only needs the name and age of the person. If we design a REST API, the endpoint will look like api/persons, which will end up fetching all the fields for the Person object. There is no easy way to say, I am interested in few fields and not the others, which means, the data is over fetched by REST API.
让我们考虑一个对象Person,它具有名称,年龄,电子邮件,contactNumber属性。 假设前端只需要这个人的名字和年龄。 如果我们设计REST API,则端点将看起来像api / persons ,最终将获取Person对象的所有字段。 没有简单的说法,我只对几个领域感兴趣,而对其他领域不感兴趣,这意味着数据被REST API过度获取。
GraphQL query given below will retrieve the value for name and age fields only for the Person Object.
下面给出的GraphQL查询将仅检索Person对象的名称和年龄字段的值。
// query
{
persons {
name
age
}
}
The result of the above query will contain exactly the fields we have requested as shown below
上面查询的结果将完全包含我们请求的字段,如下所示
// result
{
“data”: {
“persons”: [
{
“name”: “Alex”,
“age”: 25
},{
“name”: “Mac”,
“age”: 35
}
]
}
}
教程—使用Apollo服务器的GraphQL (Tutorial — GraphQL using Apollo Server)
先决条件 (Pre-Requisites)
- A computer running Linux, macOS or Windows with node.js installed 安装了node.js的运行Linux,macOS或Windows的计算机
- A web browser 网络浏览器
- Code editor, in this tutorial I am using Visual Studio Code 代码编辑器,在本教程中,我正在使用Visual Studio Code
GraphQL Apollo服务器从零开始 (GraphQL Apollo Server from scratch)
Lets expose GraphQL API over the HTTP using the Apollo Server. For this lets first create a folder with the name server. Then create a file, with the name package.json, in the newly created folder (server). Now, open the folder in the visual studio code.
让我们使用Apollo服务器通过HTTP公开GraphQL API。 为此,首先使用名称服务器创建一个文件夹。 然后在新创建的文件夹(服务器)中创建一个名为package.json的文件。 现在,在Visual Studio代码中打开文件夹。
Package.json文件 (Package.json File)
In this file, set the value for the key name of your choice. I have set it up as graphql-tutorial. That’s all we need for now. Your package.json will look like as shown in the image below:
在此文件中,为您选择的键名设置值。 我已经将其设置为graphql-tutorial。 这就是我们现在所需要的。 您的package.json如下图所示:
![Image for post](https://i-blog.csdnimg.cn/blog_migrate/903b69c7fad9c985920bd10f2ade374d.png)
安装依赖 (Installing dependencies)
We will install the following dependencies
我们将安装以下依赖项
apollo-server — serve the request over the http
apollo服务器 -通过http服务请求
graphql package — provides the core graphql functionality
graphql软件包 —提供核心的graphql功能
express — popular framework for node.js
express — node.js的流行框架
Open the terminal and navigate to the folder (server) and run the following command
打开终端并导航到文件夹(服务器)并运行以下命令
npm install apollo-server graphql express --save
Once the dependencies are installed, node_modules folder and package-lock.json file will be created in the server folder.
安装依赖项后,将在服务器文件夹中创建node_modules文件夹和package-lock.json文件。
server.js (server.js)
Now let’s start writing server. For this, first create a file server.js in the server folder. In server.js file import the following
现在让我们开始编写服务器。 为此,首先在服务器文件夹中创建一个文件server.js。 在server.js文件中导入以下内容
- express from express package 快递包裹快递
- ApolloServer and gql from apollo-server-express 来自apollo-server-express的ApolloServer和gql
- fs from fs (fs stands for file system. It is built in module for the nodejs) fs from fs(fs代表文件系统。它是为nodejs内置的模块)
After the imports, let’s set some constants.Port — Server will listen to this portPath — GraphQL Playground endpoint on the serverApp — defines that it is an express AppapolloServer — ApolloServer instance that takes typeDefs and resolvers. TypeDefs and resolvers are explained later in this post.
导入后,让我们设置一些常量。 端口 -服务器将侦听此端口。 路径 -服务器App上的GraphQL Playground端点App-定义它是一个快速的App apolloServer -ApolloServer实例,它带有typeDefs和resolver。 TypeDefs和解析器将在本文后面解释。
// server/server.jsconst express = require(‘express’);
const { ApolloServer, gql } = require(‘apollo-server-express’)
const fs = require (‘fs’)const port = 4000;
const path = ‘/graphql’
const app = express();const server = new ApolloServer({
typeDefs,
resolvers
})
Now, plug in the Apollo Server into the existing application by using applyMiddleware. We will pass app (express()) and path (graphql endpoint on the server) to the applyMiddleware function.
现在,使用applyMiddleware将Apollo服务器插入现有应用程序。 我们将app( express() )和路径( 服务器上的graphql端点 )传递给applyMiddleware函数。
// server/server.jsconst express = require(‘express’);
const { ApolloServer, gql } = require(‘apollo-server-express’)
const fs = require (‘fs’)const port = 4000;
const path = ‘/graphql’
const app = express();const server = new ApolloServer({
typeDefs,
resolvers
})server.applyMiddleware({ app, path })
Now let’s define typeDefs and resolvers for GraphQL. Firstly, we will define schema (typeDefs) and resolvers in a very simple manner. Once we test that the implementation of the endpoint is working, we will then proceed with editing the schema and resolver to retrieve Posts made by each Person using some fake data.
现在让我们为GraphQL定义typeDefs和解析器。 首先,我们将以非常简单的方式定义架构(typeDefs)和解析器。 一旦测试完端点的实现正常工作,我们将继续编辑架构和解析器,以使用假数据检索每个人发表的帖子。
TypeDefs(类型定义) (TypeDefs (Type Definitions))
The schema for GraphQL defines the hierarchy of types with fields, queries and mutations that are available. The schema for GraphQL is defined by using Schema Definition Language and is stored as a string.
GraphQL的架构使用可用的字段,查询和突变定义类型的层次结构。 GraphQL的架构是使用架构定义语言定义的,并存储为字符串。
Create a file schema.graphql in the server directory and define a type Query in it as shown below:
在服务器目录中创建文件schema.graphql,并在其中定义查询类型,如下所示:
// server/schema.graphqltype Query {}
Inside the curly brackets we will declare which field(s) belong to this type. The syntax is the name of the field followed by column and a value type. Let’s add a field person of type String
在大括号内,我们将声明属于该类型的字段。 语法是字段名称,后跟列和值类型。 让我们添加一个String类型的现场人员
// server/schema.graphqltype Query {
person: String
}
In server.js file load the schema by using the fs module. Below is the server.js file after adding the code to read the schema. We will pass encoding as utf8 so that the file is read as string and not as a binary file. readFileSync will return the contents of our file which is then parsed by the gql function and assigned to the typeDefs variable.
在server.js文件中,使用fs模块加载架构。 以下是添加代码以读取架构后的server.js文件。 我们将通过utf8编码,以便将文件读取为字符串而不是二进制文件。 readFileSync将返回文件的内容,然后由gql函数对其进行解析并分配给typeDefs变量。
// server/server.js
const express = require(‘express’);
const { ApolloServer, gql } = require(‘apollo-server-express’)
const fs = require (‘fs’)const port = 4000;
const path = ‘/graphql’
const app = express();const typeDefs = gql(fs.readFileSync(‘./schema.graphql’, {encoding: ‘utf8’}))const server = new ApolloServer({
typeDefs,
resolvers
})server.applyMiddleware({ app, path })
resolvers.js (resolvers.js)
Create a file resolvers.js in the server folder. In the resolvers file, create a Query Object that will contain the query resolvers. In the query object, we will then define the resolver function for the person field which returns a string “It works”. Finally, we will export the Query object using module.exports.
在服务器文件夹中创建一个文件resolvers.js。 在解析器文件中,创建一个将包含查询解析器的查询对象。 然后,在查询对象中,我们将为人员字段定义解析器函数,该函数返回字符串“ It works”。 最后,我们将使用module.exports导出Query对象。
// server/resolvers.jsconst Query = {
person: () => ‘It works’
}module.exports = {
Query
}
In server.js file import the resolvers module (created in the step above) and pass it to the resolvers variable.
在server.js文件中,导入resolvers模块(在上面的步骤中创建),并将其传递给resolvers变量。
// server/server.js
const express = require(‘express’);
const { ApolloServer, gql } = require(‘apollo-server-express’)
const fs = require (‘fs’)const port = 4000;
const path = ‘/graphql’
const app = express();const typeDefs = gql(fs.readFileSync(‘./schema.graphql’, {encoding: ‘utf8’}))
const resolvers = require(‘./resolvers’)const server = new ApolloServer({
typeDefs,
resolvers
})server.applyMiddleware({ app, path })
Last thing before we start the server, add the following line to the server.js file
在启动服务器之前的最后一件事,将以下行添加到server.js文件中
app.listen(port, () => console.info(`Server started on port ${port}`));
app.listen(port, () => console.info(`Server started on port ${port}`));
// server/server.jsconst express = require(‘express’);
const { ApolloServer, gql } = require(‘apollo-server-express’)
const fs = require (‘fs’)
const port = 4000;const path = ‘/graphql’
const app = express();const typeDefs = gql(fs.readFileSync(‘./schema.graphql’, {encoding: ‘utf8’}))
const resolvers = require(‘./resolvers’)const server = new ApolloServer({
typeDefs,
resolvers
})server.applyMiddleware({ app, path })app.listen(port, () => console.info(`Server started on port ${port}`));
测试第一个端点 (Testing first endpoint)
Start the server by typing in the terminal npm start server.js and you will see the following message
通过在终端中输入npm start server.js来启动服务器,您将看到以下消息
![Image for post](https://i-blog.csdnimg.cn/blog_migrate/bfcaf820fee39f7d3dac9229ceb5be53.png)
The server should be accessible on the localhost:4000 and we should be able to access the graphQL playground on the localhost:4000/graphql address. In the left panel, type the following
该服务器应该可以在localhost:4000上访问,并且我们应该能够在localhost:4000 / graphql地址上访问graphQL游乐场。 在左侧面板中,键入以下内容
{
person
}
and press Execute query button (sits between the 2 panels). Right panel will display the result for the query.
然后按执行查询按钮(位于两个面板之间)。 右面板将显示查询结果。
![Image for post](https://i-blog.csdnimg.cn/blog_migrate/2be59019024cbf28074a02d8d8ce18fe.png)
改进架构和解析器 (Improving schema and resolvers)
Lets improve the schema and resolvers file to retrieve the list of persons. For this example we will use a file to serve the data request but in the real world application we will have a database serving the API requests. Create a file db.js in the server directory and add the following lines.
让我们改进架构和解析器文件以检索人员列表。 在此示例中,我们将使用文件来处理数据请求,但在实际应用中,我们将具有用于服务API请求的数据库。 在服务器目录中创建文件db.js并添加以下行。
// server/db.jsconst persons =[{
“id”: “user001”,
“username”: “alice@script.com”,
“age”: 34,
“name”: “Alice”
},{
“id”: “user002”,
“username”: “makky@try.com”,
“age”: 31,
“name”: “Makky”
}]module.exports = {
persons
}
In the schema.graphql file, update the type query to return an array of Person. Next we will define a new type Person. In the type Person define — id, name, age, username fields. It should look like the following
在schema.graphql文件中,更新类型查询以返回Person数组。 接下来,我们将定义一个新的Person类型。 在“人员定义”类型中-ID,名称,年龄,用户名字段。 它应该如下所示
// server/schema.graphqltype Query {
persons: [Person]
}type Person {
id: ID!
name: String
age: Int
username: String
}
Now, we will update the resolvers for the Person object. In the real life project the revolver function will retrieve data from the database. For this example, it is reading data from a file (db.js).
现在,我们将更新Person对象的解析器。 在现实生活中的项目中,左轮手枪功能将从数据库中检索数据。 对于此示例,它正在从文件(db.js)读取数据。
// server/resolvers.jsconst db = require(‘./db’)const Query = {
persons: () => db.persons
}module.exports = {
Query
}
Finally, stop the server by pressing ctrl + c in the terminal and start it again with the same command — npm start server.js
最后,通过在终端中按ctrl + c来停止服务器,然后使用相同的命令再次启动它-npm start server.js
// Persons — id and name is retrieved
//人员-检索ID和名称
![Image for post](https://i-blog.csdnimg.cn/blog_migrate/540bc668224638fd8f60d1f2fbe38059.png)
// Persons — name and age is retrieved
//人员-检索姓名和年龄
![Image for post](https://i-blog.csdnimg.cn/blog_migrate/744978f7d772540bf7d3208b50e0b11e.png)
This simple implementation allows us to retrieve only the data that is required.
这种简单的实现方式使我们只能检索所需的数据。
扩展上面的示例以检索每个人发表的帖子。 (Expanding the example above to retrieve posts made by each person.)
Lets update the db.js file and add the posts related data into it. Each post has a userID (which maps to the id in the person’s object), an id for the post and the name of the post.
让我们更新db.js文件,并将帖子相关数据添加到其中。 每个帖子都有一个userID(映射到该人的对象中的ID),该帖子的ID和该帖子的名称。
// server/db.jsconst persons =[{
“id”: “user001”,
“username”: “alice@script.com”,
“age”: 34,
“name”: “Alice”
},{
“id”: “user002”,
“username”: “makky@try.com”,
“age”: 31,
“name”: “Makky”
}]const posts = [{
“id”: “01”,
“userID”: “user001”,
“name”: “An introduction to GraphQL”
},{
“id”: “012”,
“userID”: “user002”,
“name”: “Javascript for beginners”
},{
“id”: “03”,
“userID”: “user001”,
“name”: “Modular GraphQL”
},{
“id”: “055”,
“userID”: “user002”,
“name”: “Learn Reactjs”
},{
“id”: “0123”,
“userID”: “user002”,
“name”: “Angular vs Reactjs”
}]module.exports = {
persons,
posts
}
更新schema.graphql文件 (Update schema.graphql file)
In the Person type we will define new field posts that return an array of posts. Also, in the schema file we will define a new type Post.
在人员类型中,我们将定义新的字段帖子,这些帖子将返回帖子数组。 同样,在模式文件中,我们将定义一个新的Post类型。
// server/schema.graphqltype Query {
persons: [Person]
}type Person {
id: ID!
name: String
age: Int
username: String
posts: [Post]
}type Post {
id: ID!
name: String
}
更新resolvers.js (Update resolvers.js)
We will now update the resolvers to reflect the schema. As in the schema, posts is a field of type Person, we will declare a new object of type Person. In the Person object, we will then declare the resolver function for the posts.
现在,我们将更新解析器以反映架构。 就像在模式中一样,posts是一个Person类型的字段,我们将声明一个Person类型的新对象。 然后,在Person对象中,我们将为帖子声明resolver函数。
Each resolver function receives arguments. The first argument is the parent object. In this example we are resolving the posts for the person, the first argument received will be a person object. Finally, we will filter the posts by the person object.
每个解析程序函数都接收参数。 第一个参数是父对象。 在此示例中,我们正在解析人员的帖子,收到的第一个参数将是人员对象。 最后,我们将通过person对象过滤帖子。
// server/resolver.jsconst db = require(‘./db’)const Query = {
persons: () => db.persons
}const Person = {
posts: (person) => db.posts.filter(p => p.userID === person.id)
}module.exports = {
Query,
Person
}
Once these changes are made, restart the server and we should be able to retrieve the posts made by each person using the following query. Change the fields passed to the objects to retrieve the required data.
完成这些更改后,重新启动服务器,我们应该能够使用以下查询检索每个人的帖子。 更改传递给对象的字段以检索所需的数据。
![Image for post](https://i-blog.csdnimg.cn/blog_migrate/f522873e841efc6149f003ade8e9170b.png)
下一步是什么 (What’s next)
In my next posts, I will discuss about -* How to query GraphQL API with react hooks using the ApolloProvider.* Mutations using GraphQL* Modular GraphQL Approach
在我的下一篇文章中,我将讨论-*如何使用ApolloProvider通过react钩子查询GraphQL API。*使用GraphQL *进行突变*模块化GraphQL方法
翻译自: https://medium.com/@taranpreet_94321/build-a-graphql-server-with-node-js-apollo-server-cbd1a3eebbee
节点服务器和被管服务器