LayerX エンジニアブログ

LayerX の エンジニアブログです。

統合GraphQL Gatewayとtsoaで作る、バクラク新REST API基盤

バクラク事業部のAPIチームでソフトウェアエンジニアをしている @anashi です。

私たちのチームは、バクラクと外部システムとの連携を可能にするためのREST APIを開発・提供しています。

このAPIを使えば、例えば会計システムやERP、ZapierのようなiPaaS、各種ファイルストレージなど、お客様が利用されている様々なシステムとバクラクを連携させ、より組織に最適化された業務フローをデザインすることが可能になります(具体的な連携イメージに興味がある方は、ぜひ以下の記事もご覧ください!)。

note.com

さて、バクラクでREST APIを提供するのは今回が初めてではありません。以前にも、パートナー様向けにREST APIを構築した経験があります。

tech.layerx.co.jp

LayerXには「Be Animal」や「技術をまず試す」という行動指針・文化があり、それは日々開発に用いる技術の選定においても同様です。過去の経験から学びつつも、今回の外部システム連携用APIの要件に照らし合わせ、私たちは改めて最適な技術スタックを検討・選択しました。

この記事では、その結果として構築された新しい「バクラク外部システム連携用REST API」が、どのような技術的な工夫の上に成り立っているのか、そしてその選択がどのようなメリットをもたらしているのかをご紹介します。

特に、内部API Gatewayである「統合GraphQL Gateway」と、それを外部に公開するための「新しいREST API基盤」の組み合わせに焦点を当てていきます。

これらの構成はいずれも私がチームに参加したときには既に構築されていました。技術選定に関する意思決定の経緯や背景については、社内に蓄積されているArchitecture Decision Record (ADR) を通じて後からでも詳しく知ることができるので、その情報なども参考にしながら記していきたいと思います。

統合GraphQL Gateway

まず、私たちのREST APIが内部的にどのようにデータを取得・操作しているか、それは「統合 GraphQL Gateway」を通じて行われます。

バクラクは複数のプロダクト(サービス)から成り立っています。当初は各サービスがWebアプリ向けのAPIを持っていましたが、サービスが増えるにつれて認知負荷が増大し、サービス間の連携も複雑化していました。

当初の構成

この課題を解決するために導入されたのが「統合GraphQL Gateway」です。これは、全てのバックエンドサービスへのアクセスを集約する単一のGraphQLエンドポイントを提供するものです。Gatewayと各サービス間はgRPC (Connect)で通信しています。

統合GraphQL Gatewayを含むアーキテクチャ

このアーキテクチャには、以下のようなメリットがあります。

  • サービス間の依存関係解決の容易さ
    • GraphQLのModel Field Resolverを活用することで、例えば「帳票とその作成ユーザー情報」のように、異なるサービスに存在するデータ間の依存関係をGateway層でスマートに解決できます
  • N+1問題の標準的な解決
    • リソース間の依存関係を解決する際に発生しがちなN+1問題に対しても、DataLoaderというGraphQLで広く使われているパターンで対応できます
  • 再利用性
    • このGatewayは、今回のREST APIだけでなく、バクラクのWebアプリやスマホアプリなど、社内の様々なクライアントからも利用される共通基盤です。一度Gatewayに機能を追加すれば、多くのアプリケーションでその恩恵を受けられます

私たちの新しいREST API基盤は、この統合GraphQL Gatewayを内部的に利用することで、上記のようなメリットを享受しています。

ProtobufスキーマとGraphQLスキーマの二重管理問題

ここで、「バックエンドはgRPC/Protobufなのに、GatewayはGraphQL。スキーマ定義を二重に管理する必要があって大変じゃない?」と疑問に思われるかもしれません。

これは当初大きな課題でした。しかしこの課題を解決するために、 @izumin が開発したprotoc-gen-pothosというツールを活用しています。これは、Protobufの定義ファイルから、GraphQLスキーマ実装ライブラリであるPothos GraphQLで利用可能なTypeScriptの定義を自動生成するものです。

これにより、スキーマ定義の大元はProtobufに一本化され、二重管理の手間と齟齬のリスクが解消されました。

新REST API基盤: tsoa + GraphQL Codegen

さて、内部に強力な統合GraphQL Gatewayがあるとして、それをどのようにして外部向けのREST APIとして公開しているのでしょうか?

ここでも私たちは、過去の経験を踏まえ、改めて技術選定を行いました。重視したのは、API利用者の利便性を高めるOpenAPI仕様への準拠およびSpecの自動生成と、開発の柔軟性・効率性です。

以前の基盤(GraphQL Sofaを利用)で享受できていた主なメリットは以下です。

  • GraphQLスキーマを元に、OpenAPI仕様に準拠したドキュメントを自動生成できる
  • GraphQLスキーマから容易にREST APIのインターフェースを構築できる

今回の新しい基盤では、tsoaというOpenAPI準拠のフレームワークとgraphql-codegenを組み合わせることで、以下のメリットを享受できるようになりました。

  • GraphQLスキーマからの分離による、より高い自由度と柔軟性
    • REST API固有のレスポンス形式への整形や、より精密なリクエスト/レスポンスのバリデーションルールなどを、GraphQLスキーマに影響されずに実装可能
    • tsoaでアノテーションを使い、OpenAPI仕様を細かく制御可能
  • 内部GraphQL基盤との効率的かつ型安全な連携
    • graphql-codegenを用いて統合GraphQL Gatewayのスキーマから生成したTypeScriptの型定義を、tsoaのコントローラーの実装でそのまま再利用可能
    • これにより型の整合性を保ちながら、効率的にREST APIレイヤーの開発を進めることが可能

これらによって内部のGraphQL Gatewayの恩恵を受けつつ、外部には標準的で柔軟性の高いREST APIを提供するということが可能になっています。

おわりに

この記事では、バクラクの新しい外部システム連携用REST APIを支える技術スタックについて、具体的な技術選定とそのメリットををご紹介しました。

内部的には「統合GraphQL Gateway」を共通基盤とし、protoc-gen-pothosによるProtobufからのコード生成によって、サービス間の連携やスキーマの二重管理などの課題に対応しています。

新REST API基盤としては「tsoa + graphql-codegen」 を採用することで、OpenAPIへの準拠およびSpec自動生成によるAPIの使いやすさと、内部のGraphQLスキーマから適度に分離されたことによる実装の柔軟性を両立させました。

私たちは、これからも開発者体験を高め、より便利で強力なAPIを提供できるよう、技術的な挑戦を続けていきます。この記事が、LayerXの技術や文化に興味を持つ方々の参考になると嬉しいです。

バクラクで働くことに興味のある方は、ぜひ以下からご応募ください!

jobs.layerx.co.jp