为动漫宅们设计的GraphQL

graphql-basic01.jpg

我好,我是涼宮ハルヒ出生、けいおん成長的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,如下图所示。

graphql-basic02.png

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

graphql-basic03.png
graphql-basic04.png

那么,我们将在查询界面上重新发送刚才的查询。

# 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上,《无业转生》受到了很高的期待。
洛基可爱呀,洛基。啊,真想要一个神像。

graphql-basic05.png

這裡稍微提及一下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-basic06.png

实施部分

我想开始实现一个真正的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在服务器端的实施尤为关键,因此我希望将其快速地集成到各种服务中。

请参阅

    1. Annict开发者

 

    1. 在Node.js中构建一个简单的GraphQl服务器

 

    1. 模式和类型

 

    1. GraphQL的模式和类型定义

 

    ? GraphQL 速成课程(10张图片)
广告
将在 10 秒后关闭
bannerAds