2022年6月3日

NestJSとPrismaでREST APIを構築する

読了まで20分

NestJSは、Node.jsの主要なフレームワークの1つであり、最近多くの開発者からの支持と注目を集めています。この記事では、NestJS、Prisma、PostgreSQL、Swagger を使用してバックエンドの REST API を構築する方法を説明します。

Building a REST API with NestJS and Prisma

目次

はじめに

このチュートリアルでは、「Median」(シンプルなMediumクローン)というブログアプリケーションのバックエンド REST API を構築する方法を学びます。まず、新しいNestJSプロジェクトを作成することから始めます。次に、独自のPostgreSQLサーバーを起動し、Prismaを使用してそれに接続します。最後に、REST API を構築し、Swaggerでドキュメント化します。

The final application

使用する技術

このアプリケーションを構築するために以下のツールを使用します

  • NestJSをバックエンドフレームワークとして使用
  • Prismaをオブジェクトリレーショナルマッパー(ORM)として使用
  • PostgreSQLをデータベースとして使用
  • SwaggerをAPIドキュメントツールとして使用
  • TypeScriptをプログラミング言語として使用

前提条件

前提知識

これは初心者向けのチュートリアルです。ただし、このチュートリアルでは以下の知識を前提としています

  • JavaScriptまたはTypeScript(推奨)の基本的な知識
  • NestJSの基本的な知識

: NestJSに慣れていない場合は、NestJSドキュメントの概要セクションに従うことで、すぐに基本を学ぶことができます。

開発環境

このチュートリアルを進めるには、以下の準備が必要です

  • ... Node.jsがインストールされていること。
  • ... DockerまたはPostgreSQLがインストールされていること。
  • ... Prisma VSCode拡張機能がインストールされていること。(オプション)
  • ... このシリーズで提供されるコマンドを実行するために、Unixシェル(LinuxやmacOSのターミナル/シェルなど)にアクセスできること。(オプション)

注1: オプションのPrisma VSCode拡張機能は、PrismaのIntelliSenseと構文ハイライトを非常に便利にします。

注2: Unixシェルがない場合(例えば、Windowsマシンを使用している場合)でも、このチュートリアルを進めることはできますが、シェルコマンドをあなたのマシンに合わせて変更する必要があるかもしれません。

NestJSプロジェクトを生成する

まず最初に、NestJS CLIをインストールする必要があります。NestJS CLIは、NestJSプロジェクトで作業する際に非常に便利です。NestJSアプリケーションの初期化、開発、保守に役立つ組み込みユーティリティが付属しています。

NestJS CLI を使用して、空のプロジェクトを作成できます。始めるには、プロジェクトを配置したい場所で以下のコマンドを実行します

CLI は、プロジェクトのパッケージマネージャーを選択するように促します — npm を選択してください。その後、現在のディレクトリに新しい Nest JS プロジェクトが作成されます。

プロジェクトをお好みのコードエディタ(VSCodeを推奨)で開いてください。以下のファイルが表示されるはずです

作業するほとんどのコードはsrcディレクトリにあります。NestJS CLIはすでにいくつかのファイルを生成しています。注目すべきものとしては

  • src/app.module.ts: アプリケーションのルートモジュール。
  • src/app.controller.ts: 単一のルート/を持つ基本的なコントローラー。このルートはシンプルな'Hello World!'メッセージを返します。
  • src/main.ts: アプリケーションのエントリーポイント。NestJSアプリケーションを起動します。

以下のコマンドを使用してプロジェクトを開始できます

このコマンドはファイルを監視し、変更があるたびにサーバーを自動的に再コンパイルおよびリロードします。サーバーが実行されていることを確認するには、URL https://:3000/ にアクセスしてください。'Hello World!'というメッセージが表示された空のページが表示されるはずです。

: このチュートリアルを進める間、サーバーをバックグラウンドで実行したままにしてください。

PostgreSQLインスタンスを作成する

NestJSアプリケーションのデータベースとしてPostgreSQLを使用します。このチュートリアルでは、Dockerコンテナを介してPostgreSQLをマシンにインストールし、実行する方法を示します。

: Docker を使用したくない場合は、PostgreSQL インスタンスをネイティブにセットアップするか、Heroku でホスト型 PostgreSQL データベースを入手することができます。

まず、プロジェクトのメインフォルダーにdocker-compose.ymlファイルを作成します

このdocker-compose.ymlファイルは、PostgreSQLが設定されたDockerコンテナを実行するための仕様を含む設定ファイルです。ファイル内に以下の設定を作成します

この設定について理解すべき点がいくつかあります

  • imageオプションは、使用するDockerイメージを定義します。ここでは、バージョン13.5のpostgresイメージを使用しています。
  • environmentオプションは、初期化中にコンテナに渡される環境変数を指定します。ここで、コンテナが使用するユーザー名やパスワードなどの設定オプションとシークレットを定義できます。
  • volumesオプションは、ホストファイルシステムにデータを永続化するために使用されます。
  • portsオプションは、ホストマシンのポートをコンテナにマッピングします。形式は'host_port:container_port'という慣例に従います。この場合、ホストマシンのポート5432postgresコンテナのポート5432にマッピングしています。5432は慣例的にPostgreSQLが使用するポートです。

マシンのポート5432で何も実行されていないことを確認してください。postgresコンテナを起動するには、新しいターミナルウィンドウを開き、プロジェクトのメインフォルダーで以下のコマンドを実行します

すべてが正しく機能した場合、新しいターミナルウィンドウには、データベースシステムが接続を受け入れる準備ができたことを示すログが表示されます。ターミナルウィンドウには、以下のようなログが表示されるはずです

おめでとうございます🎉。これで独自のPostgreSQLデータベースを自由に操作できるようになりました!

: ターミナルウィンドウを閉じると、コンテナも停止します。これを避けるには、コマンドの最後に-dオプションを追加します(例: docker-compose up -d)。これにより、コンテナはバックグラウンドで無期限に実行されます。

Prismaをセットアップする

データベースの準備ができたので、Prismaをセットアップしましょう!

Prismaを初期化する

まず、Prisma CLIを開発依存関係としてインストールすることから始めます。Prisma CLIを使用すると、さまざまなコマンドを実行してプロジェクトと対話できます。

プロジェクト内でPrismaを初期化するには、以下を実行します

これにより、schema.prismaファイルを含む新しいprismaディレクトリが作成されます。これはデータベーススキーマを含むメインの設定ファイルです。このコマンドは、プロジェクト内に.envファイルも作成します。

環境変数を設定する

.envファイル内には、ダミーの接続文字列を持つDATABASE_URL環境変数が表示されるはずです。この接続文字列をPostgreSQLインスタンスのものに置き換えてください。

: 前のセクションで示したように Docker を使用して PostgreSQL データベースを作成しなかった場合、接続文字列は上記のものとは異なります。PostgreSQL の接続文字列形式は、Prisma ドキュメントで入手できます。

Prismaスキーマを理解する

prisma/schema.prisma を開くと、以下のデフォルトスキーマが表示されるはずです

このファイルは、Prisma がデータベーススキーマを定義するために使用する言語であるPrisma Schema Languageで記述されています。schema.prismaファイルには、主に3つのコンポーネントがあります

  • データソース: データベース接続を指定します。上記の設定は、データベースのプロバイダーがPostgreSQLであり、データベース接続文字列がDATABASE_URL環境変数で利用できることを意味します。
  • ジェネレーター: データベース用のタイプセーフなクエリビルダーであるPrisma Clientを生成することを示します。これはデータベースにクエリを送信するために使用されます。
  • データモデル: データベースのモデルを定義します。各モデルは、基となるデータベースのテーブルにマッピングされます。現在、スキーマにはモデルがありません。この部分は次のセクションで詳しく説明します。

: Prismaスキーマの詳細については、Prismaドキュメントを確認してください。

データをモデル化する

ここでアプリケーションのデータモデルを定義します。このチュートリアルでは、ブログ上の各記事を表すためにArticleモデルのみが必要です。

prisma/prisma.schemaファイル内に、Articleという名前の新しいモデルをスキーマに追加します

ここでは、いくつかのフィールドを持つArticleモデルを作成しました。各フィールドには、名前(idtitleなど)、型(IntStringなど)、およびその他のオプション属性(@id@uniqueなど)があります。フィールドは、フィールド型の後に?を追加することでオプションにできます。

idフィールドには、@idという特別な属性があります。この属性は、このフィールドがモデルの主キーであることを示します。@default(autoincrement())属性は、このフィールドが自動的にインクリメントされ、新しく作成されたすべてのレコードに割り当てられるべきであることを示します。

publishedフィールドは、記事が公開されているか、下書きモードであるかを示すフラグです。@default(false)属性は、このフィールドがデフォルトでfalseに設定されるべきであることを示します。

2つのDateTimeフィールド、createdAtupdatedAtは、記事が作成された日時と最終更新日時を追跡します。@updatedAt属性は、記事が変更されるたびにフィールドを現在のタイムスタンプで自動的に更新します。

データベースをマイグレーションする

Prismaスキーマが定義されたので、データベースに実際のテーブルを作成するためにマイグレーションを実行します。最初のマイグレーションを生成して実行するには、ターミナルで以下のコマンドを実行します

このコマンドは3つのことを行います

  1. マイグレーションの保存: Prisma Migrateはスキーマのスナップショットを取得し、マイグレーションを実行するために必要なSQLコマンドを特定します。Prismaは、SQLコマンドを含むマイグレーションファイルを新しく作成されたprisma/migrationsフォルダに保存します。
  2. マイグレーションの実行: Prisma Migrateは、マイグレーションファイル内のSQLを実行して、データベースに基となるテーブルを作成します。
  3. Prisma Clientの生成: Prismaは、最新のスキーマに基づいてPrisma Clientを生成します。Clientライブラリがインストールされていなかったため、CLIが自動的にインストールします。package.jsonファイルのdependencies内に@prisma/clientパッケージが表示されるはずです。Prisma Clientは、Prismaスキーマから自動生成されたTypeScriptクエリビルダーです。Prismaスキーマに特化しており、データベースにクエリを送信するために使用されます。

: Prisma Migrate の詳細については、Prisma ドキュメントを参照してください。

正常に完了した場合、次のようなメッセージが表示されるはずです

生成されたマイグレーションファイルを確認して、Prisma Migrateが舞台裏で何を行っているかを把握してください

: マイグレーションファイルの名前は多少異なります。

これは、PostgreSQLデータベース内にArticleテーブルを作成するために必要なSQLです。これはPrismaスキーマに基づいてPrismaによって自動的に生成され、実行されました。

データベースに初期データを投入する

現在、データベースは空です。そこで、ダミーデータでデータベースを投入するシードスクリプトを作成します。

まず、prisma/seed.tsという名前のシードファイルを作成します。このファイルには、データベースをシードするために必要なダミーデータとクエリが含まれます。

次に、シードファイル内に以下のコードを追加します

このスクリプト内では、まずPrisma Clientを初期化します。次に、prisma.upsert()関数を使用して2つの記事を作成します。upsert関数は、where条件に一致する記事がない場合にのみ、新しい記事を作成します。createクエリの代わりにupsertクエリを使用しているのは、upsertが誤って同じレコードを二度挿入しようとすることに関連するエラーを取り除くためです。

シーディングコマンドを実行する際に、Prismaにどのスクリプトを実行するかを伝える必要があります。これを行うには、prisma.seedキーをpackage.jsonファイルの末尾に追加します

seedコマンドは、以前に定義したprisma/seed.tsスクリプトを実行します。このコマンドは、ts-nodeがすでにpackage.jsonの開発依存関係としてインストールされているため、自動的に機能するはずです。

以下のコマンドでシーディングを実行します

以下の出力が表示されるはずです

: シーディングの詳細については、Prisma ドキュメントを参照してください。

Prismaサービスを作成する

NestJSアプリケーション内では、Prisma Client APIをアプリケーションから抽象化するのが良い習慣です。これを行うには、Prisma Clientを含む新しいサービスを作成します。PrismaServiceというこのサービスは、PrismaClientインスタンスをインスタンス化し、データベースに接続する役割を担います。

Nest CLIを使用すると、CLIから直接モジュールとサービスを簡単に生成できます。ターミナルで以下のコマンドを実行します

注1: 必要に応じて、サービスモジュールの概要についてNestJSドキュメントを参照してください。

注2: サーバがすでに実行されている状態でnest generateコマンドを実行すると、NestJSがError: Cannot find module './app.controller'という例外をスローする場合があります。このエラーが発生した場合は、ターミナルからrm -rf distコマンドを実行してサーバを再起動してください。

これにより、prisma.module.tsおよびprisma.service.tsファイルを含む新しいサブディレクトリ./src/prismaが生成されるはずです。サービスファイルには以下のコードが含まれるはずです

Prismaモジュールは、シングルトンインスタンスを作成し、アプリケーション全体でサービスを共有できるようにする責任があります。これを行うには、PrismaServiceprisma.module.tsファイルのexports配列に追加します

これで、PrismaModuleインポートするすべてのモジュールはPrismaServiceにアクセスでき、それを自身のコンポーネント/サービスに注入できます。これはNestJSアプリケーションの一般的なパターンです。

これでPrismaのセットアップは完了です!REST APIの構築に取り掛かることができます。

Swaggerをセットアップする

Swaggerは、OpenAPI仕様を使用してAPIを文書化するためのツールです。NestにはSwagger用の専用モジュールがあり、すぐに使用します。

必要な依存関係をインストールすることから始めます

次に、main.tsを開き、SwaggerModuleクラスを使用してSwaggerを初期化します

アプリケーションが実行中に、ブラウザを開いてhttps://:3000/apiにアクセスしてください。Swagger UIが表示されるはずです。

Swagger User Interface

ArticleモデルのCRUD操作を実装する

このセクションでは、Articleモデルの作成、読み取り、更新、削除(CRUD)操作、およびそれに伴うビジネスロジックを実装します。

RESTリソースを生成する

REST APIを実装する前に、ArticleモデルのRESTリソースを生成する必要があります。これはNest CLIを使用してすばやく実行できます。ターミナルで以下のコマンドを実行します

いくつかのCLIプロンプトが表示されます。質問に適切に答えてください

  1. このリソースにどのような名前を使用しますか(複数形、例: "users")? articles
  2. どのトランスポート層を使用しますか? REST API
  3. CRUDエントリーポイントを生成しますか? はい

これで、RESTエンドポイントのすべてのボイラープレートを含む新しいsrc/articlesディレクトリが見つかるはずです。src/articles/articles.controller.tsファイル内には、さまざまなルート(ルートハンドラーとも呼ばれます)の定義が表示されます。各リクエストを処理するためのビジネスロジックは、src/articles/articles.service.tsファイルにカプセル化されています。現在、このファイルにはダミーの実装が含まれています。

SwaggerのAPIページをもう一度開くと、次のような表示になるはずです

Auto-generated "articles" endpoints

SwaggerModuleは、ルートハンドラー上のすべての@Body()@Query()、および@Param()デコレーターを検索して、このAPIページを生成します。

PrismaClientArticlesモジュールに追加する

Articlesモジュール内でPrismaClientにアクセスするには、PrismaModuleをインポートとして追加する必要があります。ArticlesModuleに以下のimportsを追加します

これで、ArticlesService内にPrismaServiceを注入し、それを使用してデータベースにアクセスできます。これを行うには、articles.service.tsに以下のようにコンストラクタを追加します

GET /articlesエンドポイントを定義する

このエンドポイントのコントローラーはfindAllと呼ばれます。このエンドポイントは、データベース内のすべての公開済み記事を返します。findAllコントローラーは次のようになります

ArticlesService.findAll()を更新して、データベース内のすべての公開済み記事の配列を返すようにする必要があります

findManyクエリは、where条件に一致するすべてのarticleレコードを返します。

エンドポイントをテストするには、https://:3000/apiにアクセスし、GET/articlesドロップダウンメニューをクリックします。Try it outを押してから、Executeを押して結果を確認します。

: すべてのリクエストは、ブラウザで直接実行するか、RESTクライアント(Postmanなど)を介して実行することもできます。Swaggerは、ターミナルでHTTPリクエストを実行したい場合に備えて、各リクエストのcurlコマンドも生成します。

GET /articles/draftsエンドポイントを定義する

すべての非公開記事を取得するための新しいルートを定義します。NestJSはこのエンドポイントのコントローラールートハンドラーを自動的に生成しなかったため、自分で記述する必要があります。

エディタには、articlesService.findDrafts()という関数が存在しないというエラーが表示されるはずです。これを修正するには、ArticlesServicefindDraftsメソッドを実装します

GET /articles/draftsエンドポイントは、SwaggerのAPIページで利用できるようになります。

: 各エンドポイントの実装が完了したら、SwaggerのAPIページでテストすることをお勧めします。

GET /articles/:idエンドポイントを定義する

このエンドポイントのコントローラールートハンドラーはfindOneと呼ばれます。それは次のようになります

このルートは動的なidパラメータを受け入れ、それはfindOneコントローラールートハンドラーに渡されます。Articleモデルには整数型のidフィールドがあるため、idパラメータは+演算子を使用して数値にキャストする必要があります。

次に、ArticlesServicefindOneメソッドを更新して、指定されたIDを持つ記事を返すようにします

もう一度、https://:3000/api にアクセスしてエンドポイントをテストします。GET /articles/{id} ドロップダウンメニューをクリックします。Try it out を押し、id パラメータに有効な値を追加して、Execute を押して結果を確認します。

POST /articlesエンドポイントを定義する

これは新しい記事を作成するためのエンドポイントです。このエンドポイントのコントローラールートハンドラーはcreateと呼ばれます。それは次のようになります

リクエストボディでCreateArticleDto型の引数を期待していることに注意してください。DTO(Data Transfer Object)は、データがネットワーク経由でどのように送信されるかを定義するオブジェクトです。現在、CreateArticleDtoは空のクラスです。リクエストボディの形状を定義するために、これにプロパティを追加します。

@ApiPropertyデコレータは、クラスプロパティをSwaggerModuleに可視にするために必要です。これに関する詳細は、NestJSドキュメントで入手できます。

CreateArticleDtoは、Swagger APIページのSchemasの下に定義されているはずです。UpdateArticleDtoの形状は、CreateArticleDtoの定義から自動的に推論されます。したがって、UpdateArticleDtoもSwagger内に定義されています。

次に、ArticlesServicecreateメソッドを更新して、データベースに新しい記事を作成します

PATCH /articles/:idエンドポイントを定義する

このエンドポイントは、既存の記事を更新するためのものです。このエンドポイントのルートハンドラーはupdateと呼ばれます。それは次のようになります

updateArticleDtoの定義は、CreateArticleDtoPartialTypeとして定義されています。したがって、CreateArticleDtoのすべてのプロパティを持つことができます。

以前と同様に、この操作に対応するサービスメソッドを更新する必要があります

article.update操作は、指定されたidを持つArticleレコードを検索し、updateArticleDtoのデータで更新しようとします。

データベースにそのようなArticleレコードが見つからない場合、Prismaはエラーを返します。そのような場合、APIはユーザーフレンドリーなエラーメッセージを返しません。NestJSでのエラーハンドリングについては、今後のチュートリアルで学びます。

DELETE /articles/:idエンドポイントを定義する

このエンドポイントは、既存の記事を削除するためのものです。このエンドポイントのルートハンドラーはremoveと呼ばれます。それは次のようになります

以前と同様に、ArticlesServiceに移動し、対応するメソッドを更新します

これでarticlesエンドポイントの最後の操作でした。おめでとうございます、APIの準備はほぼ完了です!🎉

Swaggerでエンドポイントをグループ化する

Swaggerでarticlesエンドポイントをすべてグループ化するために、ArticlesControllerクラスに@ApiTagsデコレータを追加します

APIページでは、articlesエンドポイントがグループ化されるようになりました。

Swaggerのレスポンスタイプを更新する

Swaggerの各エンドポイントの下にあるResponsesタブを見ると、Descriptionが空であることがわかります。これは、Swaggerがどのエンドポイントのレスポンスタイプも知らないためです。これをいくつかのデコレータを使用して修正します。

まず、Swaggerが返されるentityオブジェクトの形状を識別するために使用できるエンティティを定義する必要があります。これを行うには、articles.entity.tsファイル内のArticleEntityクラスを以下のように更新します

これは、Prisma Clientによって生成されたArticle型の実装であり、各プロパティに@ApiPropertyデコレータが追加されています。

次に、コントローラーのルートハンドラーに正しいレスポンスタイプをアノテーションします。NestJSには、この目的のためのデコレータのセットがあります。

GETPATCHDELETEエンドポイントには@ApiOkResponseを、POSTエンドポイントには@ApiCreatedResponseを追加しました。typeプロパティは、戻り値を指定するために使用されます。NestJSが提供するすべてのレスポンスデコレータは、NestJSドキュメントで見つけることができます。

これで、APIページ上のすべてのエンドポイントについて、Swaggerがレスポンスタイプを適切に定義するはずです。

まとめと最終考察

おめでとうございます!NestJSを使用して基本的なREST APIを構築しました。このチュートリアルを通して、あなたは以下を学びました

  • NestJSでREST APIを構築
  • NestJSプロジェクトにPrismaをスムーズに統合
  • SwaggerとOpenAPIを使用してREST APIを文書化

このチュートリアルから得られる主な教訓の1つは、NestJSとPrismaを使用してREST APIを構築することがいかに簡単であるかということです。これは、構造が良好で、タイプセーフで、保守が容易なバックエンドアプリケーションを迅速に構築するための、非常に生産性の高いスタックです。

このプロジェクトのソースコードはGitHubで公開されています。問題にお気づきの場合は、遠慮なくリポジトリでissueを立てるか、プルリクエストを送信してください。また、Twitterで直接連絡することもできます。

次の投稿をお見逃しなく!

Prismaニュースレターに登録する

© . All rights reserved.