以下公式サイトに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 のエンドポイントを作ってみます。ただし、普通にやるだけなら公式を見れば良いだろうという話になるので、なぜか人々が作るのを嫌がるファイルのアップロード機能を入れてみます。
https://github.com/SoraKumo001/next-apollo-server
Apollo Server4
では、GraphQL の処理に executeHTTPGraphQLRequest を使用します。httpGraphQLRequest に適切な情報を載せて呼び出します。
必要な部分はパッケージから利用出来るようになっています。変換に関する具体的な処理は以下のコードを参照してください。
https://github.com/ReactLibraries/next-apollo-server/blob/master/src/index.ts
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,
},
};
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;
アップロードの処理は 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
への移行をお勧めします。