🚀
GraphQLのモックAPIがほしい (ApolloServer)
背景
現在Next.jsでSPA開発するプロジェクトに参画中なんですが、バックエンド開発にGraphQLが採用されることになりました。
本番のAPI叩く前に、ローカルでの開発中に自分でよしなにいじれるモックAPIがほしいと思った次第です。
主な使用技術
- TypeScript
- Apollo Server
- GraphQL
結論
下記のコードで実装しました。
公式のサンプルコードをヒントにアレンジしたものです。
package.json
{
"type": "module",
"scripts": {
"compile": "tsc",
"start": "npm run compile && node ./dist/index.js"
},
"devDependencies": {
"@types/node": "^18.15.11",
"typescript": "^5.0.3"
},
"dependencies": {
"@apollo/server": "^4.6.0",
"cors": "^2.8.5",
"graphql": "^16.6.0"
}
}
index.ts
import { ApolloServer } from '@apollo/server';
import { expressMiddleware } from '@apollo/server/express4';
import { ApolloServerPluginDrainHttpServer } from '@apollo/server/plugin/drainHttpServer';
import express from 'express';
import http from 'http';
import cors from 'cors';
const typeDefs = `#graphql
# Comments in GraphQL strings (such as this one) start with the hash (#) symbol.
# This "Book" type defines the queryable fields for every book in our data source.
type Book {
title: String
author: String
}
# The "Query" type is special: it lists all of the available queries that
# clients can execute, along with the return type for each. In this
# case, the "books" query returns an array of zero or more Books (defined above).
type Query {
books: [Book]
}
`;
const books = [
{
title: 'The Awakening',
author: 'Kate Chopin',
},
{
title: 'City of Glass',
author: 'Paul Auster',
},
];
const resolvers = {
Query: {
books: () => books,
},
};
const app = express();
const httpServer = http.createServer(app);
const server = new ApolloServer({
typeDefs,
resolvers,
plugins: [ApolloServerPluginDrainHttpServer({ httpServer })],
});
await server.start();
app.use(
'/graphql',
// cors<cors.CorsRequest>({ origin: ['http://localhost:3001', 'http://localhost:4000'], optionsSuccessStatus: 200 }),
cors(),
express.json(),
expressMiddleware(server),
);
await new Promise<void>((resolve) => httpServer.listen({ port: 4000 }, resolve));
console.log(`🚀 Server ready at http://localhost:4000/graphql`);
tsconfig.json
{
"compilerOptions": {
"rootDirs": ["src"],
"outDir": "dist",
"lib": ["es2020"],
"target": "es2020",
"module": "esnext",
"moduleResolution": "node",
"esModuleInterop": true,
"types": ["node"]
}
}
これで npm start
実行すればport:4000でモックサーバーが立ち上がります。
なおReactやNext.jsで呼び出す際にはフロントエンド側はこんな感じのコードでできます。
index.tsx
import { useEffect } from 'react';
import { ApolloClient, InMemoryCache, gql } from '@apollo/client';
const client = new ApolloClient({
uri: 'http://localhost:4000/graphql',
cache: new InMemoryCache(),
});
const BOOKS_QUERY = gql`
query {
books {
title
author
}
}
`;
const Home = () => {
useEffect(() => {
client
.query({ query: BOOKS_QUERY })
.then((result) => console.log(result));
}, []);
return <div></div>
}
export default Home
ポイント
expressを使う
ApolloにはstartStandaloneServer関数というものが用意されており、これを使ってモックAPIはさくっと作れるんですが公式曰くCORS設定がいじれないようです。
今回の用途だとCORSエラーになってしまうので、expressにて対応。
body-parserの使い方
Apolloの公式含め、body-parserの実装例でこういう書き方をよく見かけます。
import { json } from 'body-parser';
...(中略)...
app.use(
...(中略)...
json(),
);
しかしこれだとエラーになります。
SyntaxError: Named export 'json' not found. The requested module 'body-parser' is a CommonJS module, which may not support all module.exports as named exports.
CommonJS modules can always be imported via the default export, for example using:
import pkg from 'body-parser';
const { json } = pkg;
そもそもexpressを使う場合、標準でbody parserが搭載されているのでそっちを使います。
import express from 'express';
...(中略)...
app.use(
...(中略)...
express.json(),
);
Discussion