为动漫宅们设计的GraphQL

我好,我是涼宮ハルヒ出生、けいおん成長的Fantasista☆みゆき。
各位,你们是否使用过GraphQL?
如果只有实现过REST API的人,我建议你们借此机会尝试一下实现GraphQL。
在这篇稿件中,我想要尝试使用GraphQL向名为Annict的动漫管理应用程序发送查询,并在本地搭建Apollo Server来实现GraphQL。
先试试打一下
听百次不如亲眼见一次。我们先试试实际使用GraphQL。
让我们根据此参考链接来生成一个简单的个人令牌,以便访问Annict的终端节点。
一旦成功发行了令牌后,首先可以尝试从终端使用curl查询。
请将ACCESS_TOKEN替换为您自己的令牌。
$ curl https://api.annict.com/graphql \
-H "Authorization: bearer ACCESS_TOKEN" \
-X POST \
-d "query=query { viewer { name } }"
如果返回的是以下的JSON数据,那就没问题了!
{"data":{"viewer":{"name":"USER_NAME"}}}
下一步,让我们在GUI客户端发送查询。
GraphQL官方推荐使用GraphiQL或Insomnia,但我将使用Chrome拓展程序Altair。
首先,将方法设置为POST,URL输入为https://api.annict.com/graphql,如下图所示。

然后,在标题中输入身份验证信息。
尽管GUI客户端的用户界面可能有所不同,但在Altair中可以从左侧窗格进行设置。
作为授权,将Bearer和之前的个人访问令牌输入认证信息中。


那么,我们将在查询界面上重新发送刚才的查询。
# request
query {
viewer {
username,
name,
}
}
# response
{
"data": {
"viewer": {
"username": "USER_NAME",
"name": "NAME"
}
}
}
如果有這樣的回答,那就可以了!
此外,在Annict中可以获取到动画作品的相关信息。可以通过参考资料来确认可以获取到的内容。
让我们获取最新的2021年秋季动漫作品信息吧。
# request
query {
searchWorks(seasons: ["2021-autumn"], orderBy: { field: WATCHERS_COUNT, direction: DESC }, first: 5) {
edges {
node {
annictId
title
watchersCount
}
}
}
}
# response
{
"data": {
"searchWorks": {
"edges": [
{
"node": {
"annictId": 8200,
"title": "無職転生 ~異世界行ったら本気だす~ 第2部",
"watchersCount": 815
}
},
{
"node": {
"annictId": 7969,
"title": "鬼滅の刃 遊郭編",
"watchersCount": 805
}
},
{
"node": {
"annictId": 7669,
"title": "劇場版 ソードアート・オンライン プログレッシブ 星なき夜のアリア",
"watchersCount": 537
}
},
{
"node": {
"annictId": 7917,
"title": "ブルーピリオド",
"watchersCount": 508
}
},
{
"node": {
"annictId": 8181,
"title": "劇場版 呪術廻戦 0",
"watchersCount": 507
}
}
]
}
}
}
目前来看,在Annict上,《无业转生》受到了很高的期待。
洛基可爱呀,洛基。啊,真想要一个神像。

這裡稍微提及一下query的內容。基本上,我們將以巢狀方式處理物件。首先,我們只輸入了query。在GraphQL中,有兩個可用的指令,分別是query和mutation,query使用GET方法,mutation使用POST方法進行操作。由於我們想要獲取動畫資訊,所以我們使用了query來描述。
接下来,我们通过searchWorks函数来设置获取作品的参数。您输入了季节和搜索条件。本次我们选择2021年秋季,按照观看注册量从高到低获取了5个作品。
然后,我们描述了在edges之后获取的节点字段。
这次我们返回了动画ID、标题和观看注册数量。
那么我们趁着这个机会来获取一下2021年夏季动画的结果吧。
# request
query {
searchWorks(seasons: ["2021-summer"], orderBy: { field: WATCHERS_COUNT, direction: DESC }, first: 5) {
edges {
node {
annictId
title
watchersCount
}
}
}
}
# response
{
"data": {
"searchWorks": {
"edges": [
{
"node": {
"annictId": 6492,
"title": "小林さんちのメイドラゴンS",
"watchersCount": 2014
}
},
{
"node": {
"annictId": 7411,
"title": "転生したらスライムだった件 第2期 第2部",
"watchersCount": 1472
}
},
{
"node": {
"annictId": 7922,
"title": "白い砂のアクアトープ",
"watchersCount": 1453
}
},
{
"node": {
"annictId": 7547,
"title": "乙女ゲームの破滅フラグしかない悪役令嬢に転生してしまった…X",
"watchersCount": 1277
}
},
{
"node": {
"annictId": 7973,
"title": "探偵はもう、死んでいる。",
"watchersCount": 1196
}
}
]
}
}
}
京都动漫的《工作细胞BLACK》和《关于我转生变成史莱姆这档事》的第二季,以及P.A.WORKS的《水族馆》都非常受欢迎呢。
顺便说一下,我最喜欢的是《侦探已经死了》。
只有银发女主才能胜出。

实施部分
我想开始实现一个真正的GraphQL服务器。
环境
-
- サーバサイド言語: node v12.19.0
- GraphQLサーバ: Apollo server
我們將立即在所選的路徑中創建項目目錄。
mkdir graphql-server
npm init
安装模块。
npm install express apollo-server-express graphql --save
因为使用ES6的语法进行编写,所以需要安装babel。
npm install @babel/core @babel/node @babel/preset-env --save-dev
创建一个名为.babelrc的babel配置文件,并按以下方式进行描述。
{
"presets": [
"@babel/preset-env"
]
}
在项目的根目录下创建一个名为”src”的文件夹,并在其中的index.js文件中按照以下方式进行编写。
import express from 'express';
import { ApolloServer } from 'apollo-server-express';
import http from 'http';
const app = express();
const httpServer = http.createServer(app);
const server = new ApolloServer();
const port = process.env.PORT || 4000;
const start = async () => {
await server.start();
server.applyMiddleware({ app, path: '/graphql' });
await new Promise(resolve => httpServer.listen({ port: 4000 }, resolve));
console.log(`Server ready at http://localhost:${port}${server.graphqlPath}`);
};
start();
除了上述的代码,还需要编写模式和解析器。
模式是对字段定义类型的内容,
解析器是对端点定义函数的内容。
首先,在src文件夹中新建一个schemas文件夹和一个resolvers文件夹,并开始编写这两个文件夹中的index.js。
首先,编写resolvers文件夹中的index.js。
const db = [{ id: 1, name: 'Siesta'}]; // in memory data store
const resolvers = {
Query: {
getMe: () => {
return db[0];
},
getUserById: (parents, args) => {
const { id } = args
if (id <= 0 || id > db.length) throw new Error('User Not found.');
else {
const i = id - 1;
return db[i];
}
},
getUserByName: (parents, args) => {
const { name } = args
if (!name) throw new Error('User Not found.');
else {
for (let i in db) {
if (db[i]['name'] === name) {
const id = db[i]['id'];
const j = id - 1
return db[j];
}
}
}
},
},
Mutation: {
createUser: (parent, args) => {
const count = db.length;
const id = count + 1;
let { name } = args;
if (!name) name = 'no name';
const newUser = { id, name };
db.push(newUser);
return newUser;
},
}
};
export default resolvers;
接下来,我们将编写schemas文件夹中的index.js。
import { gql } from 'apollo-server-express';
const schema = gql`
type Query {
getMe: User
getUserById(id: ID!): User
getUserByName(name: String!): User
}
type Mutation {
createUser(
name: String!
): User!
}
type User {
id: ID!
name: String!
}
`;
export default schema;
使用这个解析器和模式来更新最初的src/index.js文件。
import express from 'express';
import { ApolloServer } from 'apollo-server-express';
import http from 'http';
/// 追記
import schemas from './schemas';
import resolvers from './resolvers';
///
const app = express();
const httpServer = http.createServer(app);
// 修正
const server = new ApolloServer({
typeDefs: schemas,
resolvers,
});
//
const port = process.env.PORT || 4000;
const starter = async () => {
await server.start();
server.applyMiddleware({ app, path: '/graphql' });
await new Promise(resolve => httpServer.listen({ port: 4000 }, resolve));
console.log(`Server ready at http://localhost:${port}${server.graphqlPath}`);
};
starter();
然后,我们将在根目录的 package.json 文件中的 scripts 键对象中添加以下内容,以便可以使用 npm start 命令。
"start": "babel-node src/index.js"
如果在控制台屏幕上显示如下,则表示成功!
Server ready at http://localhost:4000/graphql
那么,让我们来确认一下在resolvers中定义的查询和变更是否可以实际执行。
我们通过浏览器连接到本地主机并执行以下查询。
# request
query {
getUserById(id: 1) {
id, name
}
}
# response
{
"data": {
"getUser": {
"id": "1",
"name": "Siesta"
}
}
}
请注意,在常规的REST API中,query通常对应GET操作。然后,在REST API中,我们将执行mutation来对应POST操作。请注意,在GraphQL中,query和mutation都会变成POST方法。
# request
query {
createUser(name: "Roxy") {
id, name
}
}
# response
{
"data": {
"createUser": {
"id": "2",
"name": "Roxy"
}
}
}
最后,让我们确认一下,除了使用ID进行搜索用户外,还定义了可以使用用户名进行搜索的功能。
# request
query {
getUserByName(name: "Roxy") {
id,
name,
}
}
# response
{
"data": {
"getUserByName": {
"id": "2",
"name": "Roxy"
}
}
}
你已经确认了一切平安无事!
最后
由于GraphQL在服务器端的实施尤为关键,因此我希望将其快速地集成到各种服务中。
请参阅
-
- Annict开发者
-
- 在Node.js中构建一个简单的GraphQl服务器
-
- 模式和类型
-
- GraphQL的模式和类型定义
- ? GraphQL 速成课程(10张图片)