💪

Next.js 13にApollo Serverを建ててみた

2023/06/25に公開

はじめに

業務でGraphQLをApollo ServerとClientを使う機会があって、結構GraphQLを気に入りました。
が、最初の構築や細々した設定を先輩エンジニアがしてくれたため、そこら辺を全く理解せずに実装していた。そして、パフォーマンス改善やキャッシュ、認証についてもあまり理解ができてないので、個人開発でじっくり調べながら実装していこうと思いました。
今回の記事は、そんな中でNext.js 13にApollo Serverを建てた過程をまとめていきます。

使用ライブラリ

  • @apollo/server: 4.7.4
  • @as-integrations/next: 2.0.0
  • graphql: 16.6.0
  • graphql-tag: 2.12.6
  • @graphql-codegen/cli: 4.0.1
  • @graphql-codegen/schema-ast: 4.0.0
  • @graphql-codegen/typescript: 4.0.1
  • @graphql-codegen/typescript-resolvers: 4.0.1

やったこと

  1. GraphQL Code Generatorのセットアップ
  2. GraphQL schemaの記述 + 分割したschemaをマージ
  3. resolverの型生成 + resolverの記述
  4. *.graphqlをimport + Apollo Serverの初期化
  5. Next.js 13のRoute HandlersにApollo Serverを設定

GraphQL Code Generatorのセットアップ

自分がTypeScriptを使用している関係でGraphQLの型付けが必須だったので、GraphQL Code Generatorを使用して、型生成を行なっていきます。

必要なパッケージのインストール

yarn add graphql
yarn add -D @graphql-codegen/cli

設定ファイルの初期化

yarn graphql-codegen init

設定ファイル

codegen.ts
import type { CodegenConfig } from '@graphql-codegen/cli';

const config: CodegenConfig = {
  schema: './src/libs/gql/schema/**/*.graphql',
  documents: ['src/**/*.tsx', 'src/**/*.ts'],
  ignoreNoDocuments: true,
  generates: { ... },
};

export default config;

GraphQL schemaの記述 + 分割したschemaをマージ

ディレクトリ
# tsファイルにはリゾルバーを記述しています
src/libs/gql/schema
├ user
| ├ mutation
| | └ createUser.ts
| ├ query
| | └ user.ts
| └ schema.graphql
├ Mutation.ts
└ Query.ts

今回、ドメインごとにschemaを記述したかったため、このような構成にしました。
この構成の場合、Apollo ServerにtypeDefsを渡すためschemaを1つにまとめる必要がありました。
ので、@graphql-codegen/schema-astを使用

codegen.ts
const config: CodegenConfig = {
  ...
  generates: {
    './src/libs/gql/generated/schema.graphql': {
      plugins: ['schema-ast'],
    },
  },
};

これがめっちゃ便利で試した感じ、同じ型があると勝手にマージしてくれる模様。
なので、各スキーマ内で別々のQueryやMutationを書いてもモーマンタイ!!

# /user/schema.graphql
type Query {
  users: [User!]!
}

# /book/schema.graphql
type Query {
  books: [Book!]!
}

# /generated/schema.graphql(出力)
type Query {
  users: [User!]!
  books: [Book!]!
}

resolverの型生成 + resolverの記述

resolverの型を生成するために@graphql-codegen/typescript-resolversを使用。

codegen.ts
const config: CodegenConfig = {
  ...
  generates: {
    './src/libs/gql/generated/resolvers-types.ts': {
      config: {
        useIndexSignature: true,
      },
      plugins: ['typescript', 'typescript-resolvers'],
    },
  },
};

resolverを記述するとき、resolvers-typesから生成した型をimport

import { QueryResolvers } from '@/libs/gql/generated/resolvers-types';

export const userQuery: QueryResolvers['user'] = () => ({
  id: '0',
  name: 'john',
  age: 5,
});

*.graphqlをimport + Apollo Serverの初期化

@graphql-codegen/schema-astを使って1つにまとめたschemaをApollo Server初期化時にtypeDefsとして渡す必要があったので、webpackで*.graphqlファイルをimportできるように設定する。
loaderはgraphql-tag/loaderを使用。
TypeScriptを使用しているため、型定義も作成。

next.config.js
const nextConfig = {
  webpack: (config) => {
    config.module.rules.push({
      test: /\.(graphql|gql)/,
      exclude: /node_modules/,
      loader: 'graphql-tag/loader',
    });
    return config;
  },
};
index.d.ts
declare module '*.graphql' {
  export default DocumentNode;
}

これで*.graphqlをimportできるようになったので、Apollo Serverを初期化する

import typeDefs from '../generated/schema.graphql';
import { resolvers } from './resolvers';
import { ApolloServer } from '@apollo/server';

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

Next.js 13のRoute HandlersにApollo Serverを設定

Next.jsにApollo Serverを統合するのに便利な@as-integrations/nextが用意されている。
app directoryにも対応済みなので、今回はこれを使用。

/api/graphql/route.ts
import { ApolloServer } from '@apollo/server';
import { startServerAndCreateNextHandler } from '@as-integrations/next';
import { NextRequest } from 'next/server';

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

const apolloServer = startServerAndCreateNextHandler<NextRequest>(server, {
  context: async (req) => ({ req }),
});

export { apolloServer as GET, apolloServer as POST };

今後やりたいこと

  • Prisma x Vercel Postgresでデータベースを構築、Apollo Serverからデータを取得できるようにする
  • Apollo Serverへのアクセス認証・認可

Discussion