【運用中のDBとうまく付き合うBFF開発】① NestJSとPrismaで生SQLを使ってデータを取得する

image

この記事は、【 可茂IT塾 Advent Calendar 2023 】の4日目の記事です。

はじめに

2023年頭に、長らく運用しているWebアプリケーションのアプリ版を新しく作るため、Flutterアプリのバックエンドアプリケーション(APIサーバー)を開発する業務を任せていただきました。

いわゆるBFF(Backend For Frontend)の開発です。

クライアント・サーバーの関係図

この開発を通じて学んだことを共有します。3記事のシリーズになっているので合わせて以下の記事をお読みください。

  1. 📖 【運用中のDBとうまく付き合うBFF開発】① NestJSとPrismaで生SQLを使ってデータを取得する
  2. 📚 【運用中のDBとうまく付き合うBFF開発】② passport-localを使った認証とハッシュソルトの実装
  3. 📚 【運用中のDBとうまく付き合うBFF開発】③ 複雑な条件分岐に宣言的プログラミングで立ち向かう

既存DBのテーブルがもたらす制約

新しくAPIサーバーを構築するため、使用技術の選定などから開始しましたが、主にデータベースに関係する制約が強く以下のような状況がありました。

  • APIサーバーは、今現在稼働しているWebアプリケーションの都合に合わせて設計されたデータベース(Postgresql)からデータを取得・書き込みする。
  • 中間テーブルも含め関係するテーブル数とカラム数はそれなりに多い(10~15カラムあるテーブルが20個~ほどある)
  • 外部キー制約など、テーブル側で整合性を保証するための設計は基本的にされておらず、データの整合性はアプリケーション側で保証しなければならない
  • アプリケーションに依存せずに関係各所と連携して開発するために一部、ストアドファンクションを使用する

(スキーマから生成したER図。モザイクがかってますが、一部のテーブルを除いて外部キー制約が付けられていない設計なのが伝わると思います。)

スキーマ図

このような状況の中で開発を進めるにあたり、NestJSというNode.jsのバックエンドフレームワークとPrismaというORMを採用して開発を進めました。

選定理由としては、私自身が今までにAPIサーバーを開発する経験が乏しかったので、得意な言語であるTypeScriptを用いてエコシステムや現行のコミュニティの活発さによる新鮮な情報の多さを開発に活用できる点を重視しました。

スキーマ生成の活用

Prismaは、データベースのスキーマからデータモデルを生成することができます。この機能を活用することで、既存のデータベースを活用しながら、データモデルを作成することができました。

prisma db pullのコマンドを実行することで、既存のデータベースからスキーマを生成することができます。

うれしいのは、スキーマを生成するのと一緒にテーブルの型定義も生成してくれることです。 これにより、既存のデータベースのスキーマからTypeScriptの型定義を生成することができました。

$queryRawの活用

APIサーバーでのSQL操作は、参照系のクエリがほとんどでした。ので、データ取得部分の開発は以下のように進めました。

  1. 既存のテーブルを読み解いてSQLをSQLクライアントで書いて試す(TablePlusを使いました)
  2. prismaの$queryRawに生SQLを記載してデータを取得する
  3. 型は、生成された型定義からPickOmit等のジェネリクスを活用しながら組み合わせて指定する

疑似コードで示すとこのような実装の形です。

import { type example_table, prisma } from '@prisma/client';

type ExampleTableQueryResult = Array<
  Pick<
    example_table,
    'id'
  >
>;


const queryResult = await prisma.$queryRaw<ExampleTableQueryResult>`
  SELECT
    id
  FROM
    example_table;
`;

$queryRawを使うことで、Prismaの強みであるクエリ最適化の恩恵は得られませんでしたが、生SQLでデータ取得することで、最適化されたクエリで実行することができました。

振り返ってみるとこの$queryRawの活用はかなり良かったです。というのも、クエリには、ストアドファンクションを使用しなければいけなかったり、複雑なテーブルに対してサブクエリを駆使しながらクエリを書く必要がありました。

つまり、生成するSQLがかなり複雑だったのに対し、$queryRawのインターフェースは追加の学習コストや躓きポイントを作らず、ほぼ生SQLを実行するのと同等の手軽さで実装を進めることができました。

NestJSの公式ドキュメントで紹介されているTypeORM等、他のORMを使っていたら、このようには進まなかったと思いますし、既存DBに合わせてスキーマをアプリケーションで定義するだけでもかなりの実装工数がかかったと思います。

また、「1テーブルだけからwhereで条件指定してデータを取得する」と言ったような単純なクエリだったり、更新系のクエリに対しては、Prismaの他のAPIを使いながらかなり簡単に実装し、複雑な要件のデータ取得だけ生SQLを活用することができました。

おわりに

今回は、既存のデータベースを活用しながら、NestJSとPrismaを使ってAPIサーバーを開発する際に、生SQLを活用したデータ取得の方法を紹介しました。

今現在、この開発をもう一度やり直すとしたら、SQLBuilerとしてPrismaの代わりに Kyselyを検討すると思います。Kyselyは、生SQLのようなメソッドを使いながらその結果の型を生成できるそうです。

とはいえ、$queryRawに対して型を当てても単体テストやバリデーションなどの実装などの他の形でクエリの結果の形を保証したり、書き捨てのクエリは一度実行して確認すれば良いだけなので、今回のような開発には十分でした。

次の記事である【運用中のDBとうまく付き合うBFF開発】② passport-localを使った認証とハッシュソルトの実装もぜひお読みください。

お知らせ

可茂IT塾ではFlutterインターンを募集しています!

可茂IT塾ではFlutterインターンを募集しています!

可茂IT塾ではFlutterインターンを募集しています!可茂IT塾のエンジニアの判断で、一定以上のスキルをを習得した方には有給でのインターンも受け入れています。

Read More
U30可茂ITインターンハッカソン

U30可茂ITインターンハッカソン

12月28,29日開催。2日間でアプリ開発の企画から完成までを目指す!U30可茂ITインターンハッカソンを開催します。

Read More

タグ

Flutter (110)初心者向け (28)イベント (18)Google Apps Script (15)Nextjs (12)可茂IT塾 (8)Firebase (7)riverpod (6)React (6)ChatGPT (5)vscode (5)デザイン (5)新卒 (4)就活 (4)Figma (4)Dart (4)JavaScript (4)お知らせ (4)FlutterWeb (3)Prisma (3)NestJS (3)Slack (3)TypeScript (3)ワーケーション (3)インターン (3)設計 (2)線型計画法 (2)事例 (2)Git (2)Image (2)File (2)Material Design (2)画像 (2)iOS (2)アプリ開発 (2)React Hooks (2)tailwindcss (2)社会人 (2)大学生 (2)RSS (1)Google (1)Web (1)CodeRunner (1)個人開発 (1)Android (1)Unity (1)WebView (1)Twitter (1)フルリモート (1)TextScaler (1)textScaleFactor (1)学生向け (1)supabase (1)Java (1)Spring Boot (1)shell script (1)正規表現 (1)パワーポイント (1)趣味 (1)モンスターボール (1)CSS (1)SCSS (1)Cupertino (1)ListView (1)就活浪人 (1)既卒 (1)保守性 (1)iPad (1)シェアハウス (1)スクレイピング (1)PageView (1)画面遷移 (1)flutter_hooks (1)Gmail (1)GoogleWorkspace (1)ShaderMask (1)google map (1)Google Places API (1)GCPコンソール (1)Google_ML_Kit (1)Vercel (1)Google Domains (1)DeepLeaning (1)深層学習 (1)Google Colab (1)コード生成 (1)GitHub Copilot (1)オンラインオフィス (1)オブジェクト指向 (1)クラスの継承 (1)ポリモーフィズム (1)LINE (1)Bitcoin (1)bitFlyer (1)コミュニティー (1)文系エンジニア (1)Freezed (1)permission_handler (1)flutter_local_notifications (1)markdown (1)GlobalKey (1)ValueKey (1)Key (1)アイコン (1)go_router (1)debug (1)datetime_picker (1)Apple Store Connect (1)FlutterGen (1)デバッグ (1)Widget Inspector (1)検索機能 (1)Shader (1)Navigator (1)メール送信 (1)Firebase App Distribution (1)Fastlane (1)Dio (1)CustomClipper (1)ClipPath (1)カスタム認証 (1)アニメーション (1)Arduino (1)ESP32 (1)経験談 (1)フリーランス (1)mac (1)csv (1)docker (1)GithubActions (1)Dialog (1)BI (1)LifeHack (1)ショートカット (1)Chrome (1)高校生 (1)キャリア教育 (1)非同期処理 (1)生体認証 (1)BackdropFilter (1)レビュー (1)getAuth (1)Algolia (1)コンサルティング (1)Symbol (1)

お知らせ

可茂IT塾ではFlutterインターンを募集しています!

可茂IT塾ではFlutterインターンを募集しています!

可茂IT塾ではFlutterインターンを募集しています!可茂IT塾のエンジニアの判断で、一定以上のスキルをを習得した方には有給でのインターンも受け入れています。

Read More
U30可茂ITインターンハッカソン

U30可茂ITインターンハッカソン

12月28,29日開催。2日間でアプリ開発の企画から完成までを目指す!U30可茂ITインターンハッカソンを開催します。

Read More