使用apollo-server、prisma和MySQL构建GraphQL服务器

首先

我总结了使用apollo-server + prisma + MySQL构建GraphQL服务器的步骤。

apollo-server 是什么?

https://www.apollographql.com/docs/ 是指什么?

Prisma 是什么?
参考链接:https://www.prisma.io/docs/concepts/overview/what-is-prisma

所有步骤

首先需要安装 Node.js 才能继续下一步操作,接下来的步骤是使用 npm 进行操作,但您也可以根据个人喜好使用 yarn 或其他工具。首先仅使用 apollo-server 搭建 GraphQL 服务器,然后使用 prisma 与 MySQL 进行连接,最后将它们两者连接在一起。

构建apollo-server

可以按照官方的 Get started 指南快速简便地创建。请参考链接 https://www.apollographql.com/docs/apollo-server/getting-started。

按照这个步骤,每个文件应该按照以下方式进行。
※由于上述URL推荐使用TypeScript,这里也将使用它。

{
  "name": "graphql-server-example",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "type": "module",
  "scripts": {
    "compile": "tsc",
    "start": "npm run compile && node ./dist/index.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "@apollo/server": "^4.0.4",
    "graphql": "^16.6.0"
  },
  "devDependencies": {
    "@types/node": "^18.11.7",
    "typescript": "^4.8.4"
  }
}
{
  "compilerOptions": {
    "rootDirs": ["src"],
    "outDir": "dist",
    "lib": ["es2020"],
    "target": "es2020",
    "module": "esnext",
    "moduleResolution": "node",
    "esModuleInterop": true,
    "types": ["node"]
  }
}
import { ApolloServer } from '@apollo/server';
import { startStandaloneServer } from '@apollo/server/standalone';

// GraphQLサーバで利用するデータ形式(=スキーマ)を定義します。
const typeDefs = `
  # データソースのデータ形式です。
  type Book {
    title: String
    author: String
  }

  # 利用するクエリが返すデータ形式を定義します。
  # この場合、booksは0件以上のBookデータの配列を表します。
  type Query {
    books: [Book]
  }
`;

// データセットです。
// 今回は安価に作成するため、定数で定義します。
const books = [
  {
    title: 'The Awakening',
    author: 'Kate Chopin',
  },
  {
    title: 'City of Glass',
    author: 'Paul Auster',
  },
];

// クエリ実行時、どのようなデータを返すか設定(リゾルバ)をします。
// この場合、上記のデータセットをそのまま返します。
const resolvers = {
  Query: {
    books: () => books,
  },
};

// スキーマとリゾルバを指定し、apollo-server起動準備をします。
const server = new ApolloServer({
  typeDefs,
  resolvers,
});

// apollo-serverをポート番号:4000で起動します。
const { url } = await startStandaloneServer(server, {
  listen: { port: 4000 },
});

console.log(`?  Server ready at: ${url}`);

按照公式的说明,启动以下内容

% npm start
image.png

Prisma 和 MySQL 的协同配合

由于其他人已经在Docker等平台上发布了MySQL的构建过程,请参考他们的方法。本文不再对MySQL的构建进行介绍。

本次我们将根据已存在于MySQL中的表来创建schema.prisma(使用prisma db pull命令)。

我会在下面准备好表格和记录。

CREATE TABLE `test` (
  `id` bigint unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(255) NOT NULL COMMENT '名前',
  `created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '日時',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB COMMENT='テスト';

INSERT INTO `test` (`id`, `name`) VALUES ('1', 'テスト1');
INSERT INTO `test` (`id`, `name`) VALUES ('2', 'テスト2');
INSERT INTO `test` (`id`, `name`) VALUES ('3', 'テスト3');

接下来,需要安装 Prisma 客户端和 Prisma CLI。
安装后,进行 Prisma 的设置(init)。
https://www.prisma.io/docs/concepts/overview/what-is-prisma
https://www.prisma.io/docs/reference/api-reference/command-reference

% npm install @prisma/client prisma
% npx prisma init

接下来,我们需要编辑 prisma/schema.prisma 和 .env 文件,以连接到 MySQL 数据库。
https://www.prisma.io/docs/concepts/database-connectors/mysql

DATABASE_URL="mysql://USER:PASSWORD@HOST:PORT/DATABASE"
generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "mysql"
  url      = env("DATABASE_URL")
}

接下来,从 MySQL 中获取表信息,并执行 prisma 的 generate 命令。

% npx prisma db pull
% npx prisma generate

接下来,为了与apollo-server集成,我们需要编辑 src/index.ts 文件,按照以下方式进行修改。
https://www.prisma.io/apollo
https://www.prisma.io/docs/concepts/components/prisma-client/crud

import { ApolloServer } from '@apollo/server';
import { startStandaloneServer } from '@apollo/server/standalone';
import { PrismaClient } from '@prisma/client';

// prismaの利用
const prisma = new PrismaClient();

const typeDefs = `
  # 上記SQLで作成したテーブルの定義
  type Test {
    id: Int
    name: String
    created_at: String
  }

  type Query {
    Test: [Test]
  }
`;

// testテーブルのレコードを返す
const resolvers = {
  Query: {
    Test: () => prisma.test.findMany()
  },
};

const server = new ApolloServer({
  typeDefs,
  resolvers,
});

const { url } = await startStandaloneServer(server, {
  listen: { port: 4000 },
});

console.log(`?  Server ready at: ${url}`);

只需以下一种方式将其以中文进行释义:
当您再次启动 apollo-server 时,

% npm start
image.png

虽然我认为已经成功地处理了,但仍然发生了一些错误。
事实上,在 GraphQL 中默认情况下只提供了以下类型(标量类型),无法正确地获取 MySQL 的 BigInt 和 DateTime 类型。
请参考:https://www.apollographql.com/docs/apollo-server/schema/schema#scalar-types

因此,所需要的是定制标量,在此处可以找到:https://www.apollographql.com/docs/apollo-server/schema/custom-scalars

这次我们将使用 graphql-scalars 来很好地组织它们。
https://www.the-guild.dev/graphql/scalars/docs
https://www.the-guild.dev/graphql/scalars/docs/usage/apollo-server

引入graphql-scalars

本次我们将介绍graphql-scalars,它提供了各种自定义标量的支持。
https://www.the-guild.dev/graphql/scalars/docs

使用方法在这里
https://www.the-guild.dev/graphql/scalars/docs/usage/apollo-server

按照以上内容,您需要编辑 src/index.ts 如下。

import { ApolloServer } from '@apollo/server';
import { startStandaloneServer } from '@apollo/server/standalone';
import { PrismaClient } from '@prisma/client';
import {
  typeDefs as scalarTypeDefs,
  resolvers as scalarResolvers,
} from 'graphql-scalars';

const prisma = new PrismaClient();

const typeDefs = `
  type Test {
    id: BigInt
    name: String
    created_at: DateTime
  }

  type Query {
    Test: [Test]
  }
`;

const resolvers = {
  Query: {
    Test: () => prisma.test.findMany()
  },
};

const server = new ApolloServer({
  resolvers: {
    ...scalarResolvers,
    ...resolvers,
  },
  typeDefs: [
    ...scalarTypeDefs,
    typeDefs,
  ],
});

const { url } = await startStandaloneServer(server, {
  listen: { port: 4000 },
});

console.log(`?  Server ready at: ${url}`);

当您再次启动 apollo-server,

% npm start
image.png

这次成功取得了。但是尽管日期字段以JST保存在表中,却出现了UTC格式和实际时间不一致的问题。Prisma正在努力解决,但似乎进展不太活跃…

https://github.com/prisma/prisma/discussions/4153 及 https://github.com/prisma/prisma/issues/3292 这两个链接是关于 Prisma 的讨论和问题。

在尚未得到处理之前,似乎只能由API调用方进行相应处理。

结束时

通过上述步骤,我们发现可以使用现有的MySQL相对简单地构建GraphQL服务器。
除了我们介绍的内容外,还有指定记录提取条件,执行记录的插入、更新和删除等功能。但只要按照官方文档操作,也应该能够实现这些功能。

广告
将在 10 秒后关闭
bannerAds