✨
TypeScript & GraphQL でToDoアプリを開発する #3
⬅️前回の記事はこちら
Apollo Server & Apollo Client の起動
⭐️Apollo Serverの起動
backend/にsrc/index.tsを作成
index.ts
import express from 'express'; // Expressを読み込み
import http from 'http'; // HTTPサーバーモジュール ExpressをHTTPサーバーとして起動するために読み込み
import { ApolloServer } from '@apollo/server'; // GraphQLサーバーを作るためのApollo Serverのクラス
import { expressMiddleware } from '@apollo/server/express4'; // Apollo ServerをExpressと統合するためのミドルウェア
import { typeDefs } from './graphql/typeDefs'; // GraphQLのスキーマ定義(typeDefs)を読み込み
import { resolvers } from './graphql/resolvers'; // GraphQLのロジック(resolvers)を読み込み
import cors from 'cors'; // CORSを有効にするミドルウェアを読み込み
export async function startApolloServer() { // 非同期関数startApolloServerを定義してエクスポート
const app = express(); // Expressアプリケーションのインスタンス
const httpServer = http.createServer(app); // そのExpressを使ってHTTPサーバーを作成
const server = new ApolloServer({ // ApolloServerにGraphQLのスキーマとロジックを渡して、インスタンスを作成
typeDefs,
resolvers,
});
await server.start(); // ApolloServerの準備(スキーマ検証など)をおこなう
app.use( // ミドルウェアを登録するメソッド
'/graphql', // /graphqlに来たリクエストに対して以下を順番に処理する
cors(), // CORSを許可
express.json(), // JSON形式のリクエストボディをパース
expressMiddleware(server) // GraphQLリクエストをApollo Serverに渡す
);
const PORT = 4001; // ポート4001でHTTPサーバーを起動
await new Promise<void>((resolve) =>
httpServer.listen({ port: PORT }, resolve) // 非同期処理で、サーバーが立ち上がるのを待機
);
console.log(`🚀 http://localhost:${PORT}/graphql でサーバーの準備完了!`);
}
backend/src/にgraphql/typeDefs.tsを作成
typeDefs.ts
import { gql } from 'graphql-tag'
export const typeDefs = gql` // typeDefsという変数にGraphQLスキーマを定義してエクスポートする
type Todo { // Todoというデータ型(オブジェクト)を定義
id: Int!
title: String!
completed: Boolean!
}
type Query { // クエリ(読み取り)操作を定義
getTodos: [Todo!]!
}
type Mutation { // ミューテーション(更新系)操作を定義
createTodo(title: String!): Todo!
updateTodo(id: Int!, title: String, completed: Boolean): Todo!
deleteTodo(id: Int!): Boolean!
}
`
backend/src/graphql/にresolvers.tsを作成
resolvers.ts
import { PrismaClient, Todo } from '@prisma/client'
// PrismaClient のインスタンスを作成(以降、prisma オブジェクトから DB を操作できる)
const prisma = new PrismaClient()
// GraphQL のリゾルバを定義・エクスポート
export const resolvers = {
// Query(データの取得系)リゾルバ定義
Query: {
// Todo一覧を取得するクエリ
getTodos: async () => {
return prisma.todo.findMany() // Prisma 経由で Todo 全件を取得して返す
},
},
// Mutation(データの変更系)リゾルバ定義
Mutation: {
// 新しい Todo を作成するミューテーション
createTodo: async (_parent: unknown, args: { title: string }) => {
return prisma.todo.create({
data: {
title: args.title, // 引数からタイトルを受け取り
completed: false // 初期状態では未完了に設定
},
})
},
// 既存の Todo を更新するミューテーション
updateTodo: async (_parent: unknown, args: { id: number; title?: string; completed?: boolean }) => {
const { id, title, completed } = args
const dataToUpdate: Partial<Todo> = {} // 更新対象フィールドを格納するオブジェクト
if (title !== undefined) dataToUpdate.title = title // title が渡されていれば反映
if (completed !== undefined) dataToUpdate.completed = completed // completed も同様
return prisma.todo.update({
where: { id }, // id で対象レコードを特定し
data: dataToUpdate, // 指定されたフィールドのみ更新
})
},
// Todo を削除するミューテーション
deleteTodo: async (_parent: unknown, args: { id: number }) => {
await prisma.todo.delete({
where: { id: args.id }, // id で対象レコードを特定し削除
})
return true // 削除が成功したら true を返す
},
},
}
backend/src/にserver.tsを作成
server.ts
import { startApolloServer } from './index'
startApolloServer() // startApolloServerを実行
backend/package.jsonのscriptセクションでdevスクリプトを定義
package.json
// 修正前
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
package.json
// 修正後
"scripts": {
"dev": "ts-node-dev --respawn --transpile-only src/server.ts", // devスクリプトを定義
"test": "echo \"Error: no test specified\" && exit 1"
}
backend/で npm run dev を実行
% npm run dev
index.tsのconsole.log()の内容が表示されたら成功
> backend@1.0.0 dev
> ts-node-dev --respawn --transpile-only src/server.ts
[INFO] 09:24:59 ts-node-dev ver. 2.0.0 (using ts-node ver. 10.9.2, typescript ver. 5.8.3)
🚀 http://localhost:4001/graphql でサーバーの準備完了!
http://localhost:4001/graphql にアクセスすると、SANDBOX(Postmanみたいなもの)が表示される
🚀Apollo Serverの起動が完了!!!
#3のおわりに
Apollo Server の起動 お疲れさまでした。
次回は Apollo Client の起動 をします。
〜GraphQLとは、RESTとの違い〜
APIのためのクエリ言語であり、「欲しいデータだけを、1回のリクエストで取得できる」仕組みを提供します。
◼️具体例
「ブログサイトでユーザーのプロフィールと、その人が過去に書いた記事のタイトル一覧を表示する」
という画面を作りたい時
- RESTの場合
下記のように2回リクエストを送る必要があります。
そして、レスポンスに不要な情報が含まれる可能性もあります。
GET /users/1
GET /users/1/posts
- GraphQLの場合
1回のリクエストで、必要な情報だけを取得できます。
query {
user(id: 1) {
name
posts {
title
}
}
}
GraphQLは、必要なデータを柔軟に効率よく取得できる仕組みであり、RESTに比べてクライアント主導の設計がしやすくなります。特にモバイルアプリや複雑なUIを持つWebアプリと非常に相性が良く、開発の効率化に加え、パフォーマンス改善にもつながります。
Discussion