フロントエンド大好き人間にとって一番苦なのはバックエンドの実装ですよね。少なくても自分はそうです。
そこで今日はNext.jsのAPI Routesを使い、ちょー簡単にGraphQLのエンドポイントAPIを生やす方法を紹介します。
はじめに
今回使用するライブラリは以下です
- Next.js
- Prisma2 w/ Photon & Lift
- GraphQL Nexus
- Apollo Server
- Apollo Client
Prismaって何?
自分で説明するより、こちらの記事がまとまって説明してあるので、興味ある方は呼んでください。
簡単に言うと、schema.prisma
に書かれたデータベースのスキーマをベースにいい感じにORMを自動生成してくれるやつです。
上の記事でも書いてある通り、まだpreviewなので、本番環境には適していません。遊ぶ程度でどうぞ。
GraphQL Nexusって何?
GraphQL NexusはPrisma社が開発したツールであり、code-firstなgraphQLのスキーマをタイプセーフかつスケール可能にしてくれます。
従来のGraphQLサーバーの構築法は、SDL(Schema Definition Language)ファーストのアプローチが基本でした。.graphql
で終わるファイルのことですね。まずスキーマを定義して、それに対するリゾルバを別で書く。TypeScriptを使用してる場合はさらにスキーマの型を書く必要がありました。何が起こるかと言うと、スキーマを少し変更するとそれに依存する他のファイルも変更する必要があり、ある程度アプリが大きくなっていくとスケーラビリティに欠けてしまいます。
code-firstと言うことは、スキーマはコード上で定義され、SDLは自動生成されるので、スキーマを変えるごとに他のファイルも編集する、という手間が省けます。詳しくは後ほどコードで。
開発
create-next-appでNext.jsアプリを作成する
npx create-next-app next-prisma
これで簡単なNext.jsアプリが作成されました。今回はTypeScriptを使用した開発なので、とりあえずpages/index.js
をindex.tsx
に変更しnpm run dev
してみてください。Next.jsが拡張子が変わったのを検知し、tsconfig.json
を自動で作成してくれます(便利)。そしてtypescript
, @types/react
, @types/node
をインストールしろとエラーが出るのでやってください。
モジュールのインストール
npm/yarn どちらでもいいです。create-next-app
がyarnで初期モジュールをインストールするので、参考コードはyarnでやります
yarn add @prisma/photon apollo-server-micro graphql nexus nexus-prisma
yarn add --dev prisma2 ts-node
package.json
にも以下のスクリプトを追加してください。後ほど説明します。
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"generate:prisma": "prisma2 generate",
"generate:nexus": "ts-node --project tsconfig-api.json --transpile-only api/_src/schema",
"generate": "yarn generate:prisma && yarn generate:nexus",
"postinstall": "yarn generate"
}
prismaファイルの作成
ルートディレクトリにprisma
ファイルを作成します。
その中にschema.prisma
を作成し、以下のようなモデルを定義します。
generator photon {
provider = "photonjs"
}
datasource db {
provider = "postgresql"
url = env("PG_URL")
}
model User {
id Int @id
email String @unique
name String?
}
datasource
のproviderはpostgresql
としてますが、mysql
やmongodb
でも大丈夫です。好きなやつをどうぞ
env
は.env
などで定義した環境変数をみにいくので、ローカルと本番でDB分けたりできます。Nowに環境変数を追加するには、ターミナルでnowにログインして、
now secrets add pg-url "postgresql://USERNAME:PASSWORD@localhost:PORT/postgres?SCHEMA"
# now secrets add <secret-name> <secret-value>
と打つと設定できます。herokuなどで簡単にpostgresDBを作成可能です。参照するにはnext.config.js
を作成し、以下のように定義します。prisma2はbuild timeに環境変数を参照するので、build
用にも定義します。
exports.default = {
env: {
PG_URL: '@pg-url'
},
build: {
env: {
PG_URL: '@pg-url'
}
}
}
この段階で、yarn generate:prisma
を叩きます。これでnode_modules/@prisma/photon
にスキーマをもとに作成されたコードが生成されます。これにより、photon APIの基盤が生成されて、photon.users.findMany({})
みたいなAPIが使用可能となります。
ちなみに、prisma2 dev
というコマンドもあり、叩くとPrismaがスキーマファイルを監視してくれて、変更を加えると自動でphotonを再生成し、データベースのスキーマをアップデートし、そしてPrisma StudioというGUIも用意してくれます。GUIで直接レコードを編集したりできちゃいます。便利。
GraphQLサーバーの構築
データレイヤーであるPrismaの方ができたので、ORMをアクセスするAPIレイヤーのGraphQLサーバーを構築しましょう。
イメージ的には、サーバーが二つある感じ。
Next.js <----> **GraphQL Server** <----> Prisma
まず、api
フォルダをルートに作成
(普通、Next.jsでは/pages
直下にapi
フォルダを作るのだが、nowにデプロイするとphoton側とNext.js側の整合性が保てなくなり今はコネクションが張れないみたい。詳しくはこちら で追ってください。でもこうすることによりnext dev
はAPIサーバーに接続できなくなるので、now dev
を使ってnowの環境をローカルで再現して開発してください。はよ治ってほしい)
その中に、graphql.ts
,_src/schema.ts
,_src/context.ts
を作成
├── api
│ ├── _src
│ │ ├── context.ts
│ │ └── schema.ts
│ └── graphql.ts
こんな感じ。_
でプリフィックスされたファイルはサーバーレスエンドポイントとしてハンドルされないらしい。
import { Photon } from '@prisma/photon';
const photon = new Photon()
export interface Context {
photon: Photon
}
// apolloServerでphotonをcontextとして渡すと、のちにnexus側で使用可能になる
export const createContext = () => ({ photon })
import { makeSchema, objectType } from 'nexus'
import { nexusPrismaPlugin } from "nexus-prisma";
import { join } from 'path';
// __dirname使えない問題はこれで解決。詳細はこちら
// https://github.com/prisma/prisma2/issues/1021
const getPath = (fileName: string) =>
join(process.cwd(), "generated", fileName);
// objectTypeでタイプを作る。なんでも作れるが、prisma側とあわせて作るとmodel APIを提供してくれて型を補給してくれるので、便利
const User = objectType({
name: "User",
definition(t) {
t.model.email();
t.model.id();
}
})
const Query = objectType({
name: "Query",
definition(t) {
t.crud.user();
t.crud.users();
}
})
const Mutation = objectType({
name: "Mutation",
definition(t) {
t.crud.createOneUser({
alias: 'createUser'
})
}
})
export const schema = makeSchema({
types: [User, Query, Mutation],
plugins: [nexusPrismaPlugin()],
outputs: {
typegen: getPath('nexus.ts'),
schema: getPath('schema.graphql')
},
typegenAutoConfig: {
contextType: "Context.Context",
sources: [
{
source: "@prisma/photon",
alias: "photon"
},
{
source: require.resolve("./context"),
alias: "Context"
}
]
}
})
import { ApolloServer } from 'apollo-server-micro';
import { createContext } from './_src/context';
import { schema } from './_src/schema';
const apolloServer = new ApolloServer({ schema, context: createContext() })
export const config = {
api: {
bodyParser: false
}
};
export default apolloServer.createHandler({ path: '/api/graphql' });
ここで、yarn generate:nexus
を叩く前に、一旦ベースのtsconfig.json
以外に、もう一つのts-node
用のtsconfig-api.json
を作成してください。そして、その中に、
{
"extends": "./tsconfig.json",
"compilerOptions": {
"module": "commonjs"
}
}
と記述してください。なぜかと言うと、next dev
をする際に、next側はmodule": "esnext"
ではないとコンパイルしてくれないのですが、esnext
だとts-node
でコンパイルしようとしてもコケてしまいます。なので、ts-node
でコンパイルするときだけ、専用のtsconfig-api.json
を使用します。スクリプトの方で、--project tsconfig-api.json
と指定しています。
yarn generate:nexus
を叩いてください。うまくいくと、ルートにgenerated
というファイルが生成されると思います。その中にはnexus側が内部的に使用するnexus.ts
と、SDLが記述されたschema.graphql
があります。このように、普段は自分で書くはずのgraphql
ファイルが、nexusによって自動で生成されるようになります。今の段階では単純にUserのtypeしかありませんが、modelが増え、肥大化していくとその恩恵を受けます。
これで一通り終了です。now dev
を叩いてみてください。/api/graphql
のエンドポイントに行くと、graphql playgroundが開いていると思います。あとは、クライアント側でapollo clientを使うなりして、好きなようにデータを受け渡しできます。
最後に
prisma2は今現在ではまだプレビュー段階です。開発の進捗はこちらで確認できます
一つのリポジトリで、monorepoなどにする必要がなく、now
コマンド一つでバックエンドとフロントエンドをデプロイできるのって、すごい世界だなって思います。
githubとnowを連携させると、PRごとにデプロイしてくれるようになり、検証用URLもくれるので、簡単に違いをチェックできたりできます。今年はフロントエンジニアにとっては最高な年になりそうな予感。
Zeit創業者であるGuillermo Rauchさんのこのブログ記事が、今後のウェブ開発の展望など事細かく書いてあるので、おすすめです。
終わり。