使用简单的GraphQL服务器进行CRUD操作(Express.js,Objection.js,MySQL,Docker)

首先

当我开始学习GraphQL时,我感到了一丝难度?。从“如果我再次从头开始学习GraphQL的话,如果有这样一篇文章就好了”这个角度出发,我创建了一个简单的GraphQL应用,代码量很少,但功能正常。

我认为这是一个可以在20分钟内完成的实践,但是如果您很忙的话,您可以仅仅看一下”添加文件 -> 服务器”这部分?

如果在产品中使用GraphQL,我认为有很多需要考虑的事情,比如设计更具可维护性的目录结构,考虑对N+1进行优化等等。但是这次我们不去碰这些。

开发环境和所用的技术

    • OS: macOS Catalina 10.15.4

 

    • サーバー: Express.js

 

    • ORM: Objection.js(内部でKnex.jsを利用してる)

 

    • DB: MySQL8系

 

    • ツール

node: 16.8.0
yarn: 1.22.17
docker: 20.10.7
docker-compose: 1.29.2

目录结构

.
├── graphql
│   └── user.gql
├── migrations
│   └── 20211225000000_users.js
├── models
│   └── User.js
├── seeds
│   └── users.js
├── docker-compose.yml
├── knexfile.js
├── package.json
├── server.js
└── yarn.lock

亲身体验

建立环境

在创建package.json文件之后,执行yarn install安装依赖库。

{
  "version": "1.0.0",
  "dependencies": {
    "express": "^4.17.1",
    "express-graphql": "^0.12.0",
    "graphql": "^15.7.2",
    "knex": "^0.95.14",
    "mysql2": "^2.3.3",
    "objection": "^3.0.0",
    "nodemon": "^2.0.15"
  },
  "scripts": {
    "dev": "nodemon server.js"
  }
}

在创建docker-compose.yml文件后,使用docker-compose up命令来启动mysql服务器。

version: '3.8'
services:
  my-db:
    image: mysql:8
    environment:
      MYSQL_ROOT_PASSWORD: password
    ports:
      - '3306:3306'
    command: --default-authentication-plugin=mysql_native_password
    volumes:
      - ./mysql:/var/lib/mysql

使用自己喜欢的方式,在我的 my-db 容器内创建一个名为 dev_db 的数据库。

在使用docker-compose执行my-db容器并进入容器的bash客户端后,登录MySQL并执行SQL语句创建数据库。docker-compose exec my-db bash
root@1234567890:/#
root@1234567890:/# mysql -u root -p
输入密码:
〜 输入docker-compose.yml中设置的MYSQL_ROOT_PASSWORD的值 〜

欢迎来到MySQL监视器。输入命令以;或\g结尾。
〜 省略 〜
mysql>
mysql> CREATE DATABASE dev_db;
查询已受影响的行数:1(耗时0.05秒)

mysql> show databases;
+——————–+
| Database |
+——————–+
| dev_db |
| information_schema |
| mysql |
| performance_schema |
| sys |
+——————–+
结果集中的5行(耗时0.01秒)

mysql> exit
再见
root@123456789:/# exit

使用knex创建表格和虚拟数据

添加 knexfile.js 文件,并记录下与数据库的连接信息。

module.exports = {
  development: {
    client: 'mysql2',
    connection: {
      database: "dev_db",
      user: "root",
      password: "password",
    }
  }
};

运行yarn knex migrate:make users命令会生成migrations/〇〇_users.js文件。

修改生成的迁移文件

exports.up = function(knex) {
  return knex.schema.createTable('users', t => {
    t.increments('id')
    t.string('name')
    t.integer('age')
  })
};

exports.down = function(knex) {
  return knex.schema.dropTable('users')
};

运行以下命令以应用最新的 yarn knex 迁移:
yarn knex migrate:latest

使用yarn knex seed:make users来创建种子文件。

修改生成的种子文件

exports.seed = function(knex) {
  // Deletes ALL existing entries
  return knex('users').del()
    .then(function () {
      // Inserts seed entries
      return knex('users').insert([
        {id: 1, name: 'Catherine', age: 4},
        {id: 2, name: 'Mike'     , age: 8},
        {id: 3, name: 'Gonzales' , age: 16},
        {id: 4, name: 'Nancy'    , age: 32},
        {id: 5, name: 'Daniel'   , age: 64},
      ]);
    });
};

使用yarn knex seed:run命令运行seed。

添加文件

    • サーバー: server.js

参考サイト: express-graphql

const express = require("express");
const { graphqlHTTP } = require("express-graphql");
const { buildSchema } = require("graphql");
const fs = require('fs');

const schema = buildSchema(fs.readFileSync('./graphql/user.gql', 'utf8'));
const User = require('./models/User');

const readUsers = async () => {
  const users = await User.query().select();
  return users;
};

const createUser = async ({name, age}) => {
  const user = await User.query().insert({name: name, age: age});
  return user;
};

const updateUser = async ({id, name, age}) => {
  await User.query().findById(id).update({name: name, age: age});
  return User.query().findById(id);
};

const deleteUser = async ({id}) => {
  await User.query().findById(id).delete();
};

const root = {
  readUsers: readUsers,
  createUser: createUser,
  updateUser: updateUser,
  deleteUser: deleteUser,
};

const app = express();
app.use(
  "/graphql",
  graphqlHTTP({
    schema: schema,
    rootValue: root,
    graphiql: true,
  })
);
app.listen(4000, () =>
  console.log("Running a GraphQL API server at localhost:4000/graphql")
);

    GraphQLの定義ファイル: graphql/user.gql
type Query {
  readUsers: [User]
},
type Mutation {
  createUser(name: String!, age: Int): User
  updateUser(id: Int!, name: String, age: Int): User
  deleteUser(id: Int!): User
}
type User{
  id: Int
  name: String
  age: Int
}

    Objection.jsのmodelファイル: models/User.js
const { Model } = require('objection');
const knex = require('knex');
const KnexConfig = require('../knexfile');
Model.knex(knex(KnexConfig.development));

class User extends Model {
  static get tableName() {
    return 'users';
  }
}

module.exports = User;
    • yarn devコマンドで起動

 

    http://localhost:4000/graphql にアクセス

在GraphiQL网站上试着获取数据。

スクリーンショット 2021-12-22 5.47.48.png
query readUsers {
  readUsers {
    id
    name
    age
  }
}
mutation createUser($name: String!, $age: Int) {
  createUser(name: $name, age: $age) {
    ... userFields 
  }
}
fragment userFields on User {
    name
    age
}

# 以下はQUERY VARIABLESに入力
{
 "name": "Johnny",
 "age": 1
}
mutation updateUser($id: Int!, $name: String!, $age: Int) {
    updateUser(id: $id, name: $name, age: $age) {
        ... userFields 
    }
}
fragment userFields on User {
    id
    name
    age
}

# 以下はQUERY VARIABLESに入力
{
    "id": 3,
    "name": "Akira",
    "age": 2
}
mutation deleteUser($id: Int!) {
  deleteUser(id: $id) {
    ... userFields 
  }
}
fragment userFields on User {
 name
 age
}

# 以下はQUERY VARIABLESに入力
{
 "id": 5
}

最后

希望这个简单的功能能成为你接触GraphQL的契机。非常感谢你阅读到最后!

如果可以的话,请看看我们公司圣诞节日历的其他文章吧?‍♂️

广告
将在 10 秒后关闭
bannerAds