空雲リファレンス

apollo-server(v3系)は非推奨となったので、@apollo/server(v4系)に移行しましょう

ApolloServer3 のサポート終了は 2023/10/22

以下公式サイトにApollo Server 3の終了と、Apollo Server 4 移行の説明が載っています。

https://www.apollographql.com/docs/apollo-server/migration

現在使用しているパッケージがapollo-serverだった場合は非推奨バージョンです。できるだけ早く準備を整えて@apollo/serverに乗り換えましょう。

何が変わったのか

バラバラに散っていた機能が一つのパッケージに集約されました。その関係で切られる機能はバッサリ切られ、自分で書かなければならないコードが増えました。

情報が少ない

ネット上の記事はほぼApollo Server 3の頃のものばかりなので、公式以外の情報はあまり期待できません。こういう時に必要なのは、情報が少ないときほどワクワクする心を持つことです。新雪に最初に足跡を突っ込んでやるヒャッホーという気持ちこそが必要なのです。

サンプルを作ってみる

Next.js の APIRoute からアクセス出来る GraphQL のエンドポイントを作ってみます。ただし、普通にやるだけなら公式を見れば良いだろうという話になるので、なぜか人々が作るのを嫌がるファイルのアップロード機能を入れてみます。

Next.js のサンプル

https://github.com/SoraKumo001/next-apollo-server

API Route に GraphQL のエンドポイントを用意

Apollo Server4では、GraphQL の処理に executeHTTPGraphQLRequest を使用します。httpGraphQLRequest に適切な情報を載せて呼び出します。

必要な部分はパッケージから利用出来るようになっています。変換に関する具体的な処理は以下のコードを参照してください。
https://github.com/ReactLibraries/next-apollo-server/blob/master/src/index.ts

  • src/pages/api/graphql
import { promises as fs } from 'fs'; import { ApolloServer } from '@apollo/server'; import { executeHTTPGraphQLRequest, FormidableFile } from '@react-libraries/next-apollo-server'; import type { IResolvers } from '@graphql-tools/utils'; import type { NextApiHandler, NextApiRequest, NextApiResponse } from 'next'; /** * Type settings for GraphQL */ const typeDefs = ` # Return date scalar Date type Query { date: Date! } # Return file information type File { name: String! type: String! value: String! } scalar Upload type Mutation { upload(file: Upload!): File! } `; /** * Set Context type */ type Context = { req: NextApiRequest; res: NextApiResponse }; /** * Resolver for GraphQL */ const resolvers: IResolvers<Context> = { Query: { date: async (_context, _args) => new Date(), }, Mutation: { upload: async (_context, { file }: { file: FormidableFile }) => { return { name: file.originalFilename, type: file.mimetype, value: await fs.readFile(file.filepath, { encoding: 'utf8' }), }; }, }, }; /** * apolloServer */ const apolloServer = new ApolloServer<Context>({ typeDefs, resolvers, }); apolloServer.start(); /** * APIRoute handler for Next.js */ const handler: NextApiHandler = async (req, res) => { //Convert NextApiRequest to body format for GraphQL (multipart/form-data support). return executeHTTPGraphQLRequest({ req, res, apolloServer, context: async () => ({ req, res }), options: { //Maximum upload file size set at 10 MB maxFileSize: 10 * 1024 * 1024, }, }); }; export default handler; export const config = { api: { bodyParser: false, }, };

フロント側の処理

  • src/pages/_app.tsx

createUploadLink でUploadタイプのパラメータを multipart 形式に変換させる必要があります。また、ヘッダーにapollo-require-preflightが必要です。

import type { AppType } from "next/app"; import { ApolloClient, ApolloProvider, InMemoryCache } from "@apollo/client"; import { createUploadLink } from "apollo-upload-client"; const endpoint = "/api/graphql"; const uri = typeof window === "undefined" ? `${ process.env.VERCEL_URL ? `https://${process.env.VERCEL_URL}` : "http://localhost:3000" }${endpoint}` : endpoint; const App: AppType = ({ Component, pageProps }) => { const client = new ApolloClient({ cache: new InMemoryCache(), // Upload用 link: createUploadLink({ uri, headers: { "apollo-require-preflight": "true" }, }), }); return ( <ApolloProvider client={client}> <Component {...pageProps} /> </ApolloProvider> ); }; export default App;
  • src/pages/index.tsx

アップロードの処理は variables に blob オブジェクトのデータを載せるだけなので簡単です。
こちらのサンプルでは、ドラッグドロップされたデータをバックエンドに送って、内容を戻してもらい表示する実装になっています。 また、日付表示はおまけで、ファイルのアップロードとは関係ありません。

import { gql, useMutation, useQuery } from '@apollo/client'; // Date retrieval const QUERY = gql` query date { date } `; // Uploading files const UPLOAD = gql` mutation Upload($file: Upload!) { upload(file: $file) { name type value } } `; const Page = () => { const { data, refetch } = useQuery(QUERY); const [upload, { data: file }] = useMutation(UPLOAD); return ( <> <a target="_blank" href="https://github.com/SoraKumo001/next-apollo-server" rel="noreferrer"> Source code </a> <hr /> {/* SSRedacted data can be updated by refetch. */} <button onClick={() => refetch()}>Update date</button> { /* Dates are output as SSR. */ data?.date && new Date(data.date).toLocaleString('en-US', { timeZone: 'UTC' }) } {/* File upload sample from here down. */} <div style={{ height: '100px', width: '100px', background: 'lightgray', marginTop: '8px', padding: '8px', }} onDragOver={(e) => { e.preventDefault(); }} onDrop={(e) => { const file = e.dataTransfer.files[0]; if (file) { upload({ variables: { file } }); } e.preventDefault(); }} > Upload Area </div> {/* Display of information on returned file data to check upload operation. */} {file && <pre>{JSON.stringify(file, undefined, ' ')}</pre>} </> ); }; export default Page;

まとめ

Apollo Server 3は非推奨パッケージなので、早々にApollo Server 4への移行をお勧めします。