Apollo Server v4、Next.js、Prisma でGraphQLのセットアップ
はじめに
apollo-server-micro
が廃止予定になっていてApollo Server v4
を使うようになっていたので、Apollo Server v4
、Next.js
、Prisma
を使った環境構築の覚書です。
リポジトリ:https://github.com/t-shiratori/react-query-rest-graphql-prisma
apollo-server-micro は廃止予定
This package has been deprecated
Author message:
Theapollo-server-micro
package is part of Apollo Server v2 and v3, which > are now deprecated (end-of-life October 22nd 2023). This package's > functionality is now found in the@apollo/server
package. See > https://www.apollographql.com/docs/apollo-server/previous-versions/ for more > details.
-
apollo-server-micro
パッケージは、2023年10月22日に廃止予定で現在は非推奨 - このパッケージの機能は、
@apollo/server
パッケージに含まれるようなった - 詳細については、Previous versions of Apollo Server を参照
Apollo Server 4へのマイグレーションの記事
Migrating to Apollo Server 4
Next.jsでプロジェクトを作成
npx create-next-app@latest
Prismaのセットアップ
prismaをインストール
npm install prisma --save-dev
@prisma/client
をインストール
呼び出し部分の実装で使うので入れておきます。
npm i @prisma/client
参考: https://www.prisma.io/docs/concepts/components/prisma-client
初期化
npx prisma init
スキーマの定義
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model User {
id Int @id @default(autoincrement())
email String @unique
name String?
}
model Task {
id Int @id @default(autoincrement())
title String?
content String?
}
ちなみにスキーマのファイル分割についてはだいぶ前にイシューが立っていて今だに議論中のようでした。
DBと接続する
datasource db
のurlに使用するデータベースのURLを指定します。
自分は使い勝手がいいのでsupabaseを使いました。
マイグレーション
npx prisma migrate dev
prisma studio を起動する
npx prisma studio
DBと繋がっていれば以下のように prisma studio が表示されます。
REST API用のデータソースの定義
import { RESTDataSource } from '@apollo/datasource-rest'
export class JsonplaceholderUserApi extends RESTDataSource {
override baseURL = 'https://jsonplaceholder.typicode.com/'
async getUsers() {
return this.get('users')
}
async getUser(id: string) {
return this.get(`users/${id}`)
}
}
import { RESTDataSource } from '@apollo/datasource-rest'
export class JsonplaceholderPostApi extends RESTDataSource {
override baseURL = 'https://jsonplaceholder.typicode.com/'
async getPosts() {
return this.get('posts')
}
async getPost(id: string) {
return this.get(`posts/${id}`)
}
}
jsonplaceholderを使用しています。
Graphqlスキーマの定義
ファイル分割して定義します。以下を参考にしました。
#import prismaUser, createPrismaUserInput from "prismaUser.graphql"
#import jsonplaceholderPost from "jsonplaceholderPost.graphql"
#import jsonplaceholderUser from "jsonplaceholderUser.graphql"
type Query {
hello: String
prismaUser(id: ID!): prismaUser
prismaUsers: [prismaUser]
jsonplaceholderUser(id: ID!): jsonplaceholderUser
jsonplaceholderUsers: [jsonplaceholderUser]
jsonplaceholderPost(id: ID!): jsonplaceholderPost
jsonplaceholderPosts: [jsonplaceholderPost]
}
type Mutation {
prismaUser(user: createPrismaUserInput!): prismaUser
}
#import jsonplaceholderPost from "jsonplaceholderPost.graphql"
type Company {
name: String
catchPhrase: String
bs: String
}
type Geo {
lat: String
lng: String
}
type Address {
street: String
suite: String
city: String
zipcode: String
geo: Geo
}
type jsonplaceholderUser {
id: Int
name: String
username: String
email: String
phone: String
website: String
company: Company
address: Address
posts: [jsonplaceholderPost]
}
type jsonplaceholderPost {
userId: Int
id: Int
title: String
body: String
}
type prismaUser {
id: ID!
name: String!
email: String!
}
input createPrismaUserInput {
name: String!
email: String!
}
スキーマからTypeScriptの型定義を生成
以下が参考になりました。
パッケージをインストール
下記のパッケージを使います。
@graphql-codegen/cli
@graphql-codegen/typescript
@graphql-codegen/typescript-resolvers
npm install -D @graphql-codegen/cli @graphql-codegen/typescript @graphql-codegen/typescript-resolvers
設定ファイルを作成
codegen.ts
を作って以下のように定義します。
import type { CodegenConfig } from '@graphql-codegen/cli'
const config: CodegenConfig = {
overwrite: true,
schema: 'src/pages/api/graphql/schema/root.graphql',
generates: {
'src/pages/api/graphql/types/graphql.ts': {
plugins: ['typescript', 'typescript-resolvers'],
config: {
mappers: {
prismaUser: '@prisma/client/index.d#User',
},
},
},
},
}
export default config
mappers
にprisma用の指定をしています。GraphQLスキーマから生成されたデフォルトの型だとエラーになるので、代わりにPrismaのモデルを使用するようにcodegenに指示しています。
参考: GraphQL Code Generator with TypeScript and Prisma models – The Guild
コマンドで生成
npx graphql-codegen --config codegen.ts
src/pages/api/graphql/schema/root.graphql
を元にしてsrc/pages/api/graphql/types/graphql.ts
にtypescriptの型定義が出力されます。
リゾルバの定義
import { PrismaClient } from '@prisma/client'
import { JsonplaceholderPost, Resolvers } from '../types/graphql'
const prisma = new PrismaClient()
export const resolvers: Resolvers = {
Query: {
hello: () => 'Hello World',
prismaUser: async (_, args, ___, ____) => {
const result = await prisma.user.findUnique({
where: {
id: Number(args.id),
},
})
return result
},
prismaUsers: () => prisma.user.findMany(),
jsonplaceholderUser: (_, args, contextValue) => contextValue.dataSources.jsonplaceholderUserApi.getUser(args.id),
jsonplaceholderUsers: (_, __, contextValue) => contextValue.dataSources.jsonplaceholderUserApi.getUsers(),
jsonplaceholderPost: (_, args, contextValue) => contextValue.dataSources.jsonplaceholderPostApi.getPost(args.id),
jsonplaceholderPosts: (_, __, contextValue) => contextValue.dataSources.jsonplaceholderPostApi.getPosts(),
},
Mutation: {
prismaUser: async (_, args, ___, ____) => {
const { name, email } = args.user
const result = await prisma.user.create({
data: {
email,
name,
},
})
return result
},
},
jsonplaceholderUser: {
posts: async (parent, _, contextValue) => {
const allPosts: JsonplaceholderPost[] = await contextValue.dataSources.jsonplaceholderPostApi.getPosts()
const filteredPosts: JsonplaceholderPost[] = allPosts.filter(({ userId }) => userId === parent.id)
return filteredPosts
},
},
}
Next.jsのAPIルートにApolloServerを設置する
@as-integrations/nextが用意されているのでそれを使うと簡単に書けます。
@graphql-tools/load
と@graphql-tools/graphql-file-loader
は、@graphql-codegen/cli
をインストールすると一緒にインストールされます。
loadSchemaSyncで外部化したスキーマファイルを同期的に読み込むようにします。
import { ApolloServer } from '@apollo/server'
import { startServerAndCreateNextHandler } from '@as-integrations/next'
import { JsonplaceholderPostApi } from './datasources/jsonplaceholder-post'
import { JsonplaceholderUserApi } from './datasources/jsonplaceholder-use'
import { loadSchemaSync } from '@graphql-tools/load'
import { GraphQLFileLoader } from '@graphql-tools/graphql-file-loader'
import { resolvers } from './resolver'
const typeDefs = loadSchemaSync('src/pages/api/graphql/schema/root.graphql', {
loaders: [new GraphQLFileLoader()],
})
type TContextValue = {
dataSources: {
jsonplaceholderUserApi: JsonplaceholderUserApi
jsonplaceholderPostApi: JsonplaceholderPostApi
}
}
const server = new ApolloServer<TContextValue>({
typeDefs,
resolvers,
})
export default startServerAndCreateNextHandler(server, {
context: async () => ({
dataSources: {
jsonplaceholderUserApi: new JsonplaceholderUserApi(),
jsonplaceholderPostApi: new JsonplaceholderPostApi(),
},
}),
})
Apollo Explorer で確認
アプリを起動します。
yarn dev
http://localhost:3000/api/graphql
にアクセスすると以下のように表示されます。
Discussion