1. 概要
TypeScriptをベースにApollo ServerやPrisma・MySQLを使ってGraphQLの更新系APIを開発する内容となります。
対象としては開発を1年程やってて自分で最初から開発してみたい方になります。そのため細かい用語などの説明はしません。
2. 【GraphQL】Apollo ServerやTypeScriptを使ってGraphQLのAPIを開発する
こちらを参考
3. PrismaやTypeScript、MySQLを使ってデータを操作する
こちらを参考
4. nodeのインストール
こちらを参考
5. プロジェクトを作成
mkdir graphql-apollo-prisma-mysql-mutation
cd graphql-apollo-prisma-mysql-mutation
6. 必要なライブラリをインストール
npm install @apollo/server graphql ts-node
npm install --save-dev prisma typescript @types/node
npm install @graphql-tools/graphql-file-loader @graphql-tools/load @graphql-tools/schema
npm install @prisma/client
7. TypeScriptでセットアップ
npx tsc --init
npx prisma init
mkdir src
touch src/index.ts
code .
8. 設定ファイルの編集
8-1. tsconfig.json
※差し替え
{
"compilerOptions": {
"rootDirs": ["src"],
"outDir": "dist",
"lib": ["es2020"],
"target": "es2020",
"module": "esnext",
"moduleResolution": "node",
"esModuleInterop": true,
"types": ["node"]
}
}
8-2. package.json
※追加
{
"type": "module",
"scripts": {
"compile": "tsc",
"start": "npm run compile && node ./dist/index.js"
},
"dependencies": {
"@apollo/server": "^4.7.5",
"@graphql-tools/graphql-file-loader": "^8.0.0",
"@graphql-tools/load": "^8.0.0",
"@graphql-tools/schema": "^10.0.0",
"@prisma/client": "^5.0.0",
"graphql": "^16.7.1",
"ts-node": "^10.9.1"
},
"devDependencies": {
"@types/node": "^20.4.2",
"prisma": "^5.0.0",
"typescript": "^5.1.6"
}
}
9. Databaseへ接続設定
9-1. 「.env」を修正
DATABASE_URL="mysql://user:password@localhost:3306/mydb"
9-2. 「prisma/schema.prisma」を修正
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "mysql"
url = env("DATABASE_URL")
}
10. スキーマを定義
※「prisma/schema.prisma」に追加
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "mysql"
url = env("DATABASE_URL")
}
model Post {
id Int @id @default(autoincrement())
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
title String @db.VarChar(255)
content String?
published Boolean @default(false)
author User @relation(fields: [authorId], references: [id])
authorId Int
}
model Profile {
id Int @id @default(autoincrement())
bio String?
user User @relation(fields: [userId], references: [id])
userId Int @unique
}
model User {
id Int @id @default(autoincrement())
email String @unique
name String?
posts Post[]
profile Profile?
}
11. MySQLを起動
こちらを参考
起動&パーミッション付与
docker-compose up -d
docker-compose exec mysql-server bash
$ mysql -uroot -proot mysql
mysql> GRANT ALL ON *.* TO 'user';
mysql> FLUSH PRIVILEGES;
12. スキーマを作成
npx prisma migrate dev --name init
13. スキーマ定義ファイルを作成
※「src/schema.gql」
type User {
id: ID!
email: String!
name: String
posts: [Post]
profile: Profile!
}
type Profile {
id: ID!
bio: String
userId: Int!
}
type Post {
id: ID!
title: String!
content: String
published: Boolean
authorId: Int!
}
type Query {
users(published: Boolean!): [User]
}
type Mutation {
createData(name: String!, email: String!, title: String!, content: String, bio: String): User
createPost(authorId: Int!, title: String!, content: String): Post
upsertPost(id: Int!, authorId: Int!, title: String!, content: String): Post
updatePost(id: Int!, content: String!, published: Boolean!): Post
deletePost(id: Int!): Post
updateProfile(id: Int!, bio: String!): Profile
}
14. Resolverを作成
14-1-1. src/resolvers/query.ts
const users = (parent, args, contextValue, info) => {
return contextValue.prisma.user.findMany({
include: {
profile: true,
posts: {
where: {
published: args.published
}
},
}
});
}
export { users };
14-2-1. src/resolvers/mutation.ts
const createData = (parent, args, contextValue, info) => {
return contextValue.prisma.user.create({
data: {
name: args.name,
email: args.email,
posts: {
create: { title: args.title, content: args.content },
},
profile: {
create: { bio: args.bio },
}
},
});
}
const createPost = (parent, args, contextValue, info) => {
return contextValue.prisma.post.create({
data: {
authorId: Number(args.authorId),
title: args.title,
content: args.content,
},
});
}
const upsertPost = (parent, args, contextValue, info) => {
return contextValue.prisma.post.upsert({
where: {
id: Number(args.id)
},
update: {
authorId: Number(args.authorId),
title: args.title,
content: args.content
},
create: {
authorId: Number(args.authorId),
title: args.title,
content: args.content,
},
});
}
const updatePost = (parent, args, contextValue, info) => {
return contextValue.prisma.post.update({
where: {
id: Number(args.id)
},
data: {
content: args.content,
published: args.published
},
});
}
const deletePost = (parent, args, contextValue, info) => {
return contextValue.prisma.post.delete({
where: {
id: Number(args.id)
},
});
}
const updateProfile = (parent, args, contextValue, info) => {
return contextValue.prisma.profile.update({
where: {
id: Number(args.id)
},
data: {
bio: args.bio
},
});
}
export { createData, createPost, updatePost, upsertPost, deletePost, updateProfile };
15. ソースコード
※src/index.ts
import { ApolloServer } from '@apollo/server';
import { startStandaloneServer } from '@apollo/server/standalone';
import { GraphQLFileLoader } from '@graphql-tools/graphql-file-loader';
import { loadSchemaSync } from '@graphql-tools/load';
import { addResolversToSchema } from '@graphql-tools/schema';
import { PrismaClient } from '@prisma/client';
import { users } from './resolvers/query.js';
import { createData, createPost, updatePost, upsertPost, deletePost, updateProfile } from './resolvers/mutation.js';
const schema = loadSchemaSync('src/schema.gql', {
loaders: [new GraphQLFileLoader()],
});
const prisma = new PrismaClient();
const resolvers = {
Query: {
users
},
Mutation: {
createData,
createPost,
updatePost,
upsertPost,
deletePost,
updateProfile
},
};
const schemaWithResolvers = addResolversToSchema({ schema, resolvers });
const server = new ApolloServer({
schema: schemaWithResolvers
});
const { url } = await startStandaloneServer(server, {
context: async ({ req }) => ({
...req,
prisma,
}),
listen: { port: 4000 },
});
console.log(`🚀 Server ready at: ${url}`);
16. サーバーを起動
npm start
> start
> npm run compile && node ./dist/index.js
> compile
> tsc
🚀 Server ready at: http://localhost:4000/
17. クエリを実行
- http://localhost:4000
17-1. データの抽出
17-1-1. Request
query Users($published: Boolean!) {
users(published: $published) {
id
name
email
profile {
id
bio
}
posts {
id
title
content
authorId
published
}
}
}
17-1-2. Valuables
{
"published": false
}
17-2. データを作成(3つ)
17-2-1. Request
mutation CreateData($name: String!, $email: String!, $title: String!, $bio: String) {
createData(name: $name, email: $email, title: $title, bio: $bio) {
id
name
email
}
}
17-2-2. Valuables
{
"name": "Alice",
"email": "alice@prisma.io",
"title": "Hello World",
"bio": "I like turtles"
}
17-2-3. Valuables
{
"name": "John",
"email": "john@prisma.io",
"title": "Nice guy",
"bio": "Reading books"
}
17-2-4. Valuables
{
"name": "Taylor",
"email": "taylor@prisma.io",
"title": "Good morning",
"bio": "Making songs"
}
17-3. Postデータを作成(2つ)
17-3-1. Request
mutation CreatePost($authorId: Int!, $title: String!, $content: String) {
createPost(authorId: $authorId, title: $title, content: $content) {
id
title
content
authorId
published
}
}
17-3-2. Valuables
{
"authorId": 1,
"title": "Good Afternoon",
"content": "Lunch"
}
17-3-3. Valuables
{
"authorId": 2,
"title": "Too far",
"content": "100km"
}
17-4. データがなければ登録、あれば更新(今回は登録)
17-4-1. Request
mutation UpsertPost($id: Int!, $authorId: Int!, $title: String!, $content: String) {
upsertPost(id: $id, authorId: $authorId, title: $title, content: $content) {
id
title
content
authorId
published
}
}
17-4-2. Valuables
{
"id": 6,
"authorId": 2,
"title": "Drink",
"content": "Water"
}
17-5. データの更新
17-5-1. Request
mutation UpdatePost($id: Int!, $content: String!, $published: Boolean!) {
updatePost(id: $id, content: $content, published: $published) {
id
title
content
authorId
published
}
}
17-5-2. Valuables
{
"id": 6,
"content": "Water, please!",
"published": true
}
17-6. データの削除
17-6-1. Request
mutation DeletePost($id: Int!) {
deletePost(id: $id) {
id
title
content
authorId
published
}
}
17-6-2. Valuables
{
"id": 3
}
17-7. Profileデータの更新
17-7-1. Request
mutation UpdateProfile($id: Int!, $bio: String!) {
updateProfile(id: $id, bio: $bio) {
id
bio
userId
}
}
17-7-2. Valuables
{
"id": 3,
"bio": "Dogs love cats."
}
17-8. データの抽出
17-8-1. Request
query Users($published: Boolean!) {
users(published: $published) {
id
name
email
profile {
id
bio
}
posts {
id
title
content
authorId
published
}
}
}
17-8-2. Valuables
{
"published": false
}
18. MySQLでデータを確認
18-1. User
mysql> SELECT * FROM User;
+----+------------------+--------+
| id | email | name |
+----+------------------+--------+
| 1 | alice@prisma.io | Alice |
| 2 | john@prisma.io | John |
| 3 | taylor@prisma.io | Taylor |
+----+------------------+--------+
3 rows in set (0.01 sec)
18-2. Profile
mysql> SELECT * FROM Profile;
+----+-----------------+--------+
| id | bio | userId |
+----+-----------------+--------+
| 1 | I like turtles | 1 |
| 2 | Reading books | 2 |
| 3 | Dogs love cats. | 3 |
+----+-----------------+--------+
3 rows in set (0.00 sec)
18-3. Post
mysql> SELECT * FROM Post;
+----+-------------------------+-------------------------+----------------+----------------+-----------+----------+
| id | createdAt | updatedAt | title | content | published | authorId |
+----+-------------------------+-------------------------+----------------+----------------+-----------+----------+
| 1 | 2023-07-22 02:06:52.788 | 2023-07-22 02:06:52.788 | Hello World | NULL | 0 | 1 |
| 2 | 2023-07-22 02:07:03.704 | 2023-07-22 02:07:03.704 | Nice guy | NULL | 0 | 2 |
| 4 | 2023-07-22 02:07:35.773 | 2023-07-22 02:07:35.773 | Good Afternoon | Lunch | 0 | 1 |
| 5 | 2023-07-22 02:07:43.433 | 2023-07-22 02:07:43.433 | Too far | 100km | 0 | 2 |
| 6 | 2023-07-22 02:07:58.859 | 2023-07-22 02:08:13.204 | Drink | Water, please! | 1 | 2 |
+----+-------------------------+-------------------------+----------------+----------------+-----------+----------+
5 rows in set (0.00 sec)
19. ディレクトリの構造
.
├── dist
│ ├── index.d.ts
│ ├── index.js
│ └── resolvers
│ ├── mutation.d.ts
│ ├── mutation.js
│ ├── query.d.ts
│ └── query.js
├── package-lock.json
├── package.json
├── prisma
│ ├── migrations
│ │ ├── 20230717125916_init
│ │ │ └── migration.sql
│ │ └── migration_lock.toml
│ └── schema.prisma
├── src
│ ├── index.ts
│ ├── resolvers
│ │ ├── mutation.ts
│ │ └── query.ts
│ └── schema.gql
└── tsconfig.json
7 directories, 16 files
20. 備考
Prismaを使用してGraphQLの更新系APIを使いMySQLデータを操作する内容でした。
21. 参考
投稿者プロフィール
-
開発好きなシステムエンジニアです。
卓球にハマってます。