👌

お前らまだ簡単なCRUD処理を実装してるのか? 〜PostGraphileへの誘い〜

2023/07/28に公開

煽りタイトルみたいになってしまいましたが、最近の開発で使ってるPostGraphileがめちゃくちゃオススメなので、紹介記事です。

https://www.graphile.org/postgraphile/introduction/

PostGraphileとは

PostGraphileは、Node.jsベースのオープンソースのライブラリで、PostgreSQLデータベースをGraphQL APIに変換するツールです。PostGraphileは、GraphQLサーバーのセットアップやGraphQLスキーマの手動作成などを省略して、効率的にGraphQL APIを生成することができるため、開発をスピードアップできます。

主な特徴とメリット:

  • 自動生成されたGraphQL API: データベースのスキーマを基にして、自動的にGraphQLスキーマとリゾルバーを生成します。これにより、データベースに対してGraphQLクエリを直接発行することができます。
  • 安全性: PostGraphileはセキュリティに重点を置いており、権限設定をサポートしています。データベースのテーブルとカラムごとにアクセス権限を制御することができます。
  • グラフィQL UI: PostGraphileは開発中に便利なツールとして、GraphQLのクエリを簡単にテストするためのグラフィQLインターフェースを提供します。
  • スキーマ自動更新: データベースのスキーマが変更された場合、PostGraphileは自動的にGraphQLスキーマを更新し、変更内容を反映します。
  • カスタマイズ性: 必要に応じてPostGraphileの動作をカスタマイズすることができます。プラグインや設定オプションを活用して、カスタムリゾルバーやGraphQLの拡張機能を追加するなど、APIの振る舞いを調整することができます。

PostGraphileを使用することで、GraphQLを理解しやすいAPIとしてデータベースに統合し、効率的で柔軟なデータアクセスを実現することができます。実際のGUIを見た方が直感的に理解できると思います。

image

  • 左側:PostgreSQLにあるデータスキーマを読み込んで生成してくれます。
  • 中央:左のチェックボックスの操作によってgqlを自動生成します。QueryがGETメソッド(READ)、MutationがPOSTメソッド(CREATE、UPDATE、DELETE)に対応しています。オプションやプラグインによってSubscriptionも設定可能です。
  • 右側:中央のgqlの実行結果を返します。

Hasuraとの違いは?

GraphQL EngineとしてはHasuraの方が有名です。PostGraphileとHasuraはどちらもGraphQL APIを提供するツールですが、異なるアプローチを持っています。以下に主な違いを挙げます:

  • アーキテクチャとセルフホスト性:
    • Hasura: Dockerイメージとして配布され、独立してデプロイして実行します。
    • PostGraphile: PostGraphileはNode.jsベースのライブラリであり、ほとんどのケースでNodeのバックエンドサーバーに組み込むか、カスタムのGraphQLサーバーに統合する必要があります。したがって、PostGraphileを使う場合は、自分でGraphQLサーバーを構築してホストする必要があります。
  • データベースの自動スキーマ生成:
    • Hasura: データベースのスキーマを自動的に読み取り、GraphQL APIを自動生成します。データベースのテーブルとカラムが直接GraphQLの型とリゾルバーにマッピングされます。
    • PostGraphile: こちらもデータベースからGraphQLスキーマを自動生成しますが、さらにカスタマイズを行うための設定オプションが豊富にあります。また、プラグインを使ってカスタムリゾルバーやGraphQLの拡張を追加できます。
  • セキュリティと認証:
    • Hasura: デフォルトでデータベースの権限をGraphQL APIに反映し、カスタムロールや認証をサポートしています。アクセス制御を柔軟に設定できます。
    • PostGraphile: PostGraphileもセキュリティ機能を提供していますが、Hasuraほど簡単には設定できません。カスタム認証やアクセス権限の設定は、追加の設定やプラグインを使って実装する必要があります。

これらはHasuraとPostGraphileの主な違いですが、使用ケースやプロジェクトの要件に応じてどちらを選択するかは異なる場合があります。Hasuraはデータベースから直接GraphQL APIを素早く構築することに焦点を当てており、簡単な構築が必要な場合に適しています。一方、PostGraphileはカスタマイズ性が高く、既存のGraphQLサーバーとの統合が必要な場合や、高度なカスタマイズが必要な場合に適しています。PostGraphileの方が若干手間が多いですが、カスタマイズ性が高く、玄人好みといえます。

私の関わっているプロダクトでは、セキュリティ上にRLS(Row Level Security)という機能が必要だったため、HasuraではなくPostGraphileを採用するに至りました。

https://www.graphile.org/postgraphile/postgresql-schema-design/#row-level-security

NestJS + PostGraphile

NestJSにPostGraphileライブラリを組み込んでみます。

まずはNestJSプロジェクトを作成します。

$ npm i -g @nestjs/cli
$ nest new nest-graphile

プロジェクト名はnest-graphile、パッケージ管理はpnpmで進めます。

次に、PostGraphileをインストールします。

$ cd nest-graphile
$ pnpm add postgraphile

NestJSにPostGraphileを組み込んでいきます。以下のページを参考に作成しました。
https://www.graphile.org/postgraphile/usage-library/

NestJSの場合は、Route Handlerを個別にマウントして使用します。
まず、PostGraphileミドルウェアを作成します。ここでPostGraphileのオプションを定義することになるので、開発用に推奨されているオプションを設定しておきます。

src/common/middleware/postgraphile.middleware.ts
import { postgraphile } from 'postgraphile';

export const middleware = postgraphile(process.env.DATABASE_URL, 'public', {
  subscriptions: true,
  watchPg: true,
  dynamicJson: true,
  setofFunctionsContainNulls: false,
  ignoreRBAC: false,
  showErrorStack: 'json',
  extendedErrors: ['hint', 'detail', 'errcode'],
  appendPlugins: [require('@graphile-contrib/pg-simplify-inflector')],
  exportGqlSchemaPath: 'schema.graphql',
  graphiql: true,
  enhanceGraphiql: true,
  enableQueryBatching: true,
  legacyRelations: 'omit',
});

次に、汎用のPostGraphileルートハンドラーを、指定されたフレームワークに適したハンドラーに変換するアダプターが必要です。NestJSには、PostGraphileResponseNodeというアダプターが用意されています。

src/app.controller.ts
import { Controller, Get, Post, Req, Next, Res } from '@nestjs/common';
import { Request, Response } from 'express';
import { PostGraphileResponseNode } from 'postgraphile';
import { middleware } from './common/middleware/postgraphile.middleware';

@Controller('/')
export class PostGraphileController {
  @Get(middleware.graphiqlRoute)
  graphiql(@Req() request: Request, @Res() response: Response, @Next() next) {
    middleware.graphiqlRouteHandler(
      new PostGraphileResponseNode(request, response, next),
    );
  }

  @Post(middleware.graphqlRoute)
  graphql(@Req() request: Request, @Res() response: Response, @Next() next) {
    middleware.graphqlRouteHandler(
      new PostGraphileResponseNode(request, response, next),
    );
  }
}

最後に、app.moduleのcontollerに、作成したPostGraphileControllerを追加すれば完成です。

src/app.module.ts
import { Module } from '@nestjs/common';
import { PostGraphileController } from './app.controller';
import { AppService } from './app.service';

@Module({
  imports: [],
  controllers: [PostGraphileController],
  providers: [AppService],
})
export class AppModule {}

まとめ

この記事では、PostGraphileについて紹介してきました。Hasuraを代表とするGraphQL Engineは、簡単なCRUD操作のAPIを自動生成してくれるので、開発工数を大きく減らしてくれます。Hasuraと比べてもPostGraphileの方が手間は増えますが、痒い所に手が届く上、NestJSといったフレームワークに組み込むことで、これ一つで様々な操作に対応できます。

案件の大小に関わらず様々な開発現場で有用だと思いますので、ぜひ一度使ってみてください。

フィシルコム

Discussion