PostgreSQLからGraphQLサーバーを生成するPostgraphileを手元で動かしてみる
Postgraphile とは
PostgreSQL サーバーのスキーマ情報を読み取って自動的に GraphQL サーバーを立ち上げてくれる CLI/Node.js ライブラリです。
同じようなことをしてくれるものに Hasura があり、Hasura が Docker Image で配布されていて独立したサーバーとして立ち上がるのに対し Postgraphile は Node.js の HTTP サーバーに組み込まれるところが大きな違いです。
なぜ Postgraphile について調べたか
Netflix が社内サービスを立ち上げるときに Postgraphile を使って開発効率を高めたという記事を書いています。
Postgraphile で TODO アプリが作れるとかではなく実サービスの開発に適用できないかなと考えていて、クライアントサイドは GraphQL で高い DX を得られサーバーサイドはサーバー実装にかかかるコストを減らせるのではないかと思っています。ほとんどの会社は Netflix ほど巨大な開発組織ではないですが参考になる点も多いのではないかと思っています。
Postgraphile を立ち上げてみる
以下のサイトで DVD レンタルサービスを題材にしたサンプルの PostgreSQL データが配布されていて、それを Docker Image にしたものを使います。
version: "2"
services:
postgres:
image: dexels/dvdrental:1
ports:
- "5432:5432"
environment:
POSTGRES_PASSWORD: mysecretpassword
docker-compose up -d
CLI から立ち上げる
Postgraphile は CLI から起動できます。
npx postgraphile -c \
'postgres://postgres:mysecretpassword@localhost:5432/dvdrental' \
--watch --enhance-graphiql --dynamic-json
これで http://localhost:5000/graphiql にアクセスすると GraphiQL が立ち上がっています。
Relay のCursor Connections Specificationが実装されていて便利ですね。
Node.js の HTTP サーバーから立ち上げる
Node.js の http モジュールや express のミドルウェアとして使うことができます。他にも Fastify や Koa などにも対応しています。doc
const express = require("express");
const { postgraphile } = require("postgraphile");
const app = express();
app.use(
postgraphile(
process.env.DATABASE_URL ||
"postgres://postgres:mysecretpassword@localhost:5432/dvdrental",
"public",
{
watchPg: true,
graphiql: true,
enhanceGraphiql: true,
}
)
);
app.listen(process.env.PORT || 5000);
Postgraphile が提供する Query, Mutation
Query
DB からレコードを引いてくる感覚で使える Query が提供されます。
公式やサードパーティの Plugin を使うと Aggregate やより高度なフィルターなどが使えます。
query QueryExample($after: Cursor!, $first: Int!, $actorId: Int!) {
# PKで1件引いてくる
actor(actorId: $actorId) {
firstName
lastName
}
# Relay Cursor Specを実装したリスト (offsetベースのページングもできる)
actors(after: $after, first: $first) {
edges {
cursor
node {
firstName
lastName
}
}
}
# FKでJOINして引いてくる
actorAndFilm: actor(actorId: $actorId) {
firstName
lastName
filmActors(first: $first) {
edges {
node {
film {
title
}
}
}
}
}
# 簡単なフィルター
searchActor: actors(condition: { firstName: "Cuba" }) {
edges {
node {
firstName
lastName
}
}
}
}
Mutation
INSERT/UPDATE/DELETE ができます。
mutation MutationExample {
createActor(input: { actor: { firstName: "lol", lastName: "yay" } }) {
actor {
id
firstName
lastName
}
}
updateActor(input: { actorId: 10, patch: { firstName: "no" } }) {
actor {
id
firstName
lastName
}
}
deleteActor(input: { actorId: 10 }) {
actor {
id
firstName
lastName
}
}
}
Discussion