NestJSは著名なNode.jsフレームワークの一つであり、最近多くの開発者からの支持と注目を集めています。この記事では、NestJS、Prisma、PostgreSQL、Swaggerを使用してバックエンドREST APIを構築する方法を学びます。
目次
- はじめに
- 前提条件
- NestJSプロジェクトの生成
- PostgreSQLインスタンスの作成
- Prismaの設定
- Swaggerの設定
- ArticleモデルのCRUD操作の実装
- Swaggerレスポンスタイプの更新
- まとめと最終的な注意
はじめに
このチュートリアルでは、「Median」(シンプルなMediumクローン)と呼ばれるブログアプリケーションのバックエンドREST APIを構築する方法を学びます。まず、新しいNestJSプロジェクトを作成することから始めます。次に、独自のPostgreSQLサーバーを起動し、Prismaを使用して接続します。最後に、REST APIを構築し、Swaggerでドキュメント化します。
使用する技術
このアプリケーションを構築するために、次のツールを使用します。
- バックエンドフレームワークとしてのNestJS
- ORM(オブジェクト・リレーショナル・マッパー)としてのPrisma
- データベースとしてのPostgreSQL
- APIドキュメントツールとしてのSwagger
- プログラミング言語としてのTypeScript
前提条件
前提知識
これは初心者向けのチュートリアルです。ただし、このチュートリアルは以下を前提としています。
- JavaScriptまたはTypeScriptの基本的な知識(推奨)
- NestJSの基本的な知識
注:NestJSに慣れていない場合は、NestJSドキュメントの概要セクションに従って、基本をすぐに学ぶことができます。
開発環境
このチュートリアルに従うには、以下が必要です。
- ... Node.jsがインストールされていること。
- ... DockerまたはPostgreSQLがインストールされていること。
- ... Prisma VSCode Extensionがインストールされていること(オプション)。
- ... このシリーズで提供されるコマンドを実行するために、Unixシェル(LinuxおよびmacOSのターミナル/シェルなど)にアクセスできること(オプション)。
注1:オプションのPrisma VSCode拡張機能は、Prismaに非常に優れたIntelliSenseと構文ハイライトを追加します。
注2:Unixシェルがない場合(たとえば、Windowsマシンを使用している場合)、それでもチュートリアルに従うことはできますが、シェルコマンドをマシンに合わせて変更する必要がある場合があります。
NestJSプロジェクトの生成
最初に必要なのは、NestJS CLIをインストールすることです。NestJS CLIは、NestJSプロジェクトを操作する際に非常に役立ちます。NestJSアプリケーションの初期化、開発、および保守に役立つ組み込みユーティリティが付属しています。
NestJS CLIを使用して、空のプロジェクトを作成できます。開始するには、プロジェクトを配置する場所で次のコマンドを実行します。
CLIは、プロジェクトのパッケージマネージャーを選択するように求めます—npmを選択してください。その後、現在のディレクトリに新しいNestJSプロジェクトが作成されます。
プロジェクトをお好みのコードエディター(VSCodeを推奨)で開きます。次のファイルが表示されるはずです。
作業するコードのほとんどは、src
ディレクトリに配置されます。NestJS CLIは、いくつかのファイルをすでに作成しています。注目すべきファイルの一部は次のとおりです。
src/app.module.ts
:アプリケーションのルートモジュール。src/app.controller.ts
:単一のルート/
を持つ基本的なコントローラー。このルートは、シンプルな'Hello World!'
メッセージを返します。src/main.ts
:アプリケーションのエントリーポイント。NestJSアプリケーションを起動します。
次のコマンドを使用してプロジェクトを開始できます。
このコマンドはファイルを監視し、変更を加えるたびにサーバーを自動的に再コンパイルおよびリロードします。サーバーが実行されていることを確認するには、URL http://localhost:3000/
にアクセスしてください。「Hello World!」というメッセージが表示された空白のページが表示されるはずです。
注:このチュートリアルを進める間、サーバーをバックグラウンドで実行し続ける必要があります。
PostgreSQLインスタンスの作成
NestJSアプリケーションのデータベースとしてPostgreSQLを使用します。このチュートリアルでは、Dockerコンテナを介してPostgreSQLをマシンにインストールして実行する方法を示します。
注:Dockerを使用したくない場合は、PostgreSQLインスタンスをネイティブに設定するか、HerokuでホストされているPostgreSQLデータベースを取得できます。
まず、プロジェクトのメインフォルダーに docker-compose.yml
ファイルを作成します。
この docker-compose.yml
ファイルは、PostgreSQLの設定が内部にあるDockerコンテナを実行するための仕様を含む構成ファイルです。ファイル内に次の構成を作成します。
この構成について理解すべき点がいくつかあります。
image
オプションは、使用するDockerイメージを定義します。ここでは、postgres
イメージバージョン13.5を使用しています。environment
オプションは、初期化中にコンテナに渡される環境変数を指定します。コンテナが使用する構成オプションとシークレット(ユーザー名やパスワードなど)をここで定義できます。volumes
オプションは、ホストファイルシステムにデータを永続化するために使用されます。ports
オプションは、ホストマシンからコンテナへのポートをマッピングします。形式は、「host_port:container_port」の規則に従います。この場合、ホストマシンのポート5432
をpostgres
コンテナのポート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インスタンスの接続文字列に置き換えます。
注:PostgreSQLデータベースを作成するためにdocker(前のセクションで示したように)を使用しなかった場合、接続文字列は上に示したものとは異なります。PostgreSQLの接続文字列形式は、Prismaドキュメントで入手できます。
Prismaスキーマの理解
prisma/schema.prisma
を開くと、次のデフォルトスキーマが表示されるはずです。
このファイルはPrismaスキーマ言語で記述されており、これはPrismaがデータベーススキーマを定義するために使用する言語です。schema.prisma
ファイルには、主に3つのコンポーネントがあります。
- データソース:データベース接続を指定します。上記の構成は、データベースプロバイダーがPostgreSQLであり、データベース接続文字列が
DATABASE_URL
環境変数で利用可能であることを意味します。 - ジェネレーター:データベース用のタイプセーフクエリビルダーであるPrisma Clientを生成することを示します。これは、データベースにクエリを送信するために使用されます。
- データモデル:データベースモデルを定義します。各モデルは、基盤となるデータベースのテーブルにマッピングされます。現在、スキーマにはモデルはありません。この部分については、次のセクションで詳しく説明します。
注:Prismaスキーマの詳細については、Prismaドキュメントを確認してください。
データモデリング
ここで、アプリケーションのデータモデルを定義します。このチュートリアルでは、ブログの各記事を表すArticle
モデルのみが必要です。
prisma/prisma.schema
ファイル内で、Article
という名前の新しいモデルをスキーマに追加します。
ここでは、いくつかのフィールドを持つArticle
モデルを作成しました。各フィールドには、名前(id
、title
など)、型(Int
、String
など)、およびその他のオプション属性(@id
、@unique
など)があります。フィールドをオプションにするには、フィールド型の後に?
を追加します。
id
フィールドには、@id
と呼ばれる特別な属性があります。この属性は、このフィールドがモデルの主キーであることを示します。@default(autoincrement())
属性は、このフィールドが自動的にインクリメントされ、新しく作成されたレコードに割り当てられる必要があることを示します。
published
フィールドは、記事が公開されているかドラフトモードであるかを示すフラグです。@default(false)
属性は、このフィールドがデフォルトでfalse
に設定される必要があることを示します。
2つのDateTime
フィールド、createdAt
とupdatedAt
は、記事がいつ作成されたか、最後にいつ更新されたかを追跡します。@updatedAt
属性は、記事が変更されるたびに、現在のタイムスタンプでフィールドを自動的に更新します。
データベースの移行
Prismaスキーマが定義されたので、移行を実行してデータベースに実際のテーブルを作成します。最初の移行を生成して実行するには、ターミナルで次のコマンドを実行します。
このコマンドは3つのことを行います。
- 移行の保存:Prisma Migrateは、スキーマのスナップショットを取得し、移行を実行するために必要なSQLコマンドを把握します。Prismaは、SQLコマンドを含む移行ファイルを、新しく作成された
prisma/migrations
フォルダーに保存します。 - 移行の実行:Prisma Migrateは、移行ファイル内のSQLを実行して、データベースに基盤となるテーブルを作成します。
- 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によって自動的に生成および実行されました。
データベースのシード
現在、データベースは空です。そのため、ダミーデータでデータベースをpopulateするシードスクリプトを作成します。
まず、prisma/seed.ts
という名前のシードファイルを作成します。このファイルには、データベースをシードするために必要なダミーデータとクエリが含まれます。
次に、シードファイル内に次のコードを追加します。
このスクリプト内では、最初にPrisma Clientを初期化します。次に、prisma.upsert()
関数を使用して2つの記事を作成します。upsert
関数は、where
条件に一致する記事がない場合にのみ、新しい記事を作成します。create
クエリの代わりにupsert
クエリを使用しているのは、upsert
が同じレコードを誤って2回挿入しようとしたことに関連するエラーを削除するためです。
シーディングコマンドを実行するときに実行するスクリプトをPrismaに指示する必要があります。package.json
ファイルの最後にprisma.seed
キーを追加することで、これを行うことができます。
seed
コマンドは、以前に定義したprisma/seed.ts
スクリプトを実行します。ts-node
はpackage.json
に開発依存関係としてすでにインストールされているため、このコマンドは自動的に機能するはずです。
次のコマンドでシーディングを実行します。
次の出力が表示されるはずです。
注:シーディングの詳細については、Prismaドキュメントを参照してください。
Prismaサービスの作成
NestJSアプリケーション内では、Prisma Client APIをアプリケーションから抽象化することをお勧めします。これを行うには、Prisma Clientを含む新しいサービスを作成します。PrismaService
と呼ばれるこのサービスは、PrismaClient
インスタンスをインスタンス化し、データベースに接続する役割を担います。
Nest CLIを使用すると、モジュールとサービスをCLIから直接簡単に生成できます。ターミナルで次のコマンドを実行します。
注2:場合によっては、サーバーがすでに実行されている状態で
nest generate
コマンドを実行すると、NestJSが「Error: Cannot find module './app.controller'
」という例外をスローする可能性があります。このエラーが発生した場合は、ターミナルから次のコマンドを実行します:rm -rf dist
を実行してサーバーを再起動します。
これにより、prisma.module.ts
ファイルとprisma.service.ts
ファイルを含む新しいサブディレクトリ./src/prisma
が生成されるはずです。サービスファイルには、次のコードが含まれているはずです。
Prismaモジュールは、PrismaService
のシングルトンインスタンスを作成し、アプリケーション全体でサービスを共有できるようにする役割を担います。これを行うには、prisma.module.ts
ファイルのexports
配列にPrismaService
を追加します。
これで、PrismaModule
をインポートするモジュールは、PrismaService
にアクセスできるようになり、独自のコンポーネント/サービスに注入できます。これは、NestJSアプリケーションの一般的なパターンです。
これで、Prismaの設定は完了です。REST APIの構築に取り掛かることができます。
Swaggerの設定
Swaggerは、OpenAPI仕様を使用してAPIをドキュメント化するツールです。NestにはSwagger専用のモジュールがあり、まもなく使用します。
まず、必要な依存関係をインストールします。
次に、main.ts
を開き、SwaggerModule
クラスを使用してSwaggerを初期化します。
アプリケーションが実行されている間に、ブラウザを開いてhttp://localhost:3000/api
に移動します。Swagger UIが表示されるはずです。
ArticleモデルのCRUD操作の実装
このセクションでは、Article
モデルの作成、読み取り、更新、および削除(CRUD)操作と、付随するビジネスロジックを実装します。
RESTリソースの生成
REST APIを実装する前に、Article
モデルのRESTリソースを生成する必要があります。これは、Nest CLIを使用してすばやく行うことができます。ターミナルで次のコマンドを実行します。
CLIプロンプトがいくつか表示されます。質問に適切に答えてください。
What name would you like to use for this resource (plural, e.g., "users")?
articlesWhat transport layer do you use?
REST APIWould you like to generate CRUD entry points?
Yes
これで、RESTエンドポイントのすべてのボイラープレートを含む新しいsrc/articles
ディレクトリが見つかるはずです。src/articles/articles.controller.ts
ファイル内には、さまざまなルート(ルートハンドラーとも呼ばれます)の定義が表示されます。各リクエストを処理するためのビジネスロジックは、src/articles/articles.service.ts
ファイルにカプセル化されています。現在、このファイルにはダミーの実装が含まれています。
Swagger APIページを再度開くと、次のようなものが表示されるはずです。
SwaggerModule
は、ルートハンドラーのすべての@Body()
、@Query()
、および@Param()
デコレーターを検索して、このAPIページを生成します。
ArticlesモジュールへのPrismaClientの追加
Articles
モジュール内でPrismaClient
にアクセスするには、PrismaModule
をインポートとして追加する必要があります。次のimports
をArticlesModule
に追加します。
これで、PrismaService
をArticlesService
内に注入し、それを使用してデータベースにアクセスできます。これを行うには、articles.service.ts
に次のようなコンストラクターを追加します。
GET /articles エンドポイントの定義
このエンドポイントのコントローラーはfindAll
と呼ばれます。このエンドポイントは、データベース内の公開されているすべての記事を返します。findAll
コントローラーは次のようになります。
データベース内のすべての公開済み記事の配列を返すようにArticlesService.findAll()
を更新する必要があります。
findMany
クエリは、where
条件に一致するすべてのarticle
レコードを返します。
エンドポイントをテストするには、http://localhost:3000/api
に移動し、GET/articlesドロップダウンメニューをクリックします。「Try it out」を押してから「Execute」を押して結果を確認します。
注:すべてのリクエストをブラウザで直接実行するか、RESTクライアント(Postmanなど)を介して実行することもできます。Swaggerは、ターミナルでHTTPリクエストを実行する場合に備えて、各リクエストのcurlコマンドも生成します。
GET /articles/drafts エンドポイントの定義
未公開の記事をすべて取得するための新しいルートを定義します。NestJSはこのエンドポイントのコントローラールートハンドラーを自動的に生成しなかったため、自分で記述する必要があります。
エディターは、articlesService.findDrafts()
という関数が存在しないというエラーを表示するはずです。これを修正するには、ArticlesService
に findDrafts
メソッドを実装してください。
GET /articles/drafts
エンドポイントが Swagger API ページで利用可能になります。
注意: 各エンドポイントの実装が完了したら、Swagger API ページでテストすることを推奨します。
GET /articles/:id
エンドポイントを定義する
このエンドポイントのコントローラールートハンドラーは findOne
と呼ばれます。以下のようになります。
このルートは動的な id
パラメーターを受け入れ、それは findOne
コントローラールートハンドラーに渡されます。Article
モデルは integer 型の id
フィールドを持っているため、id
パラメーターは +
演算子を使用して数値にキャストする必要があります。
次に、ArticlesService
の findOne
メソッドを更新して、指定された ID の記事を返すようにします。
もう一度、http://localhost: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 内で定義されています。
次に、データベースに新しい記事を作成するために、ArticlesService
の create
メソッドを更新します。
PATCH /articles/:id
エンドポイントを定義する
これは既存の記事を更新するためのエンドポイントです。このエンドポイントのルートハンドラーは update
と呼ばれます。以下のようになります。
updateArticleDto
の定義は、CreateArticleDto
の PartialType
として定義されています。したがって、CreateArticleDto
のすべてのプロパティを持つことができます。
先ほどと同様に、この操作に対応するサービスメソッドを更新する必要があります。
article.update
操作は、指定された id
を持つ Article
レコードを検索し、updateArticleDto
のデータで更新しようとします。
データベース内にそのような Article
レコードが見つからない場合、Prisma はエラーを返します。そのような場合、API はユーザーフレンドリーなエラーメッセージを返しません。NestJS でのエラー処理については、今後のチュートリアルで学習します。
DELETE /articles/:id
エンドポイントを定義する
これは既存の記事を削除するためのエンドポイントです。このエンドポイントのルートハンドラーは remove
と呼ばれます。以下のようになります。
先ほどと同様に、ArticlesService
に移動し、対応するメソッドを更新します。
これで articles
エンドポイントの最後の操作です。おめでとうございます!API はほぼ完成です!🎉
Swagger でエンドポイントをグループ化する
すべての articles
エンドポイントを Swagger でグループ化するために、ArticlesController
クラスに @ApiTags
デコレーターを追加します。
API ページには、articles
エンドポイントがグループ化されて表示されるようになりました。
Swagger のレスポンス型を更新する
Swagger の各エンドポイントの下にある Responses タブを見ると、Description が空であることがわかります。これは、Swagger がどのエンドポイントのレスポンス型も認識していないためです。これをいくつかのデコレーターを使用して修正します。
まず、Swagger が返される entity
オブジェクトの形状を識別するために使用できるエンティティを定義する必要があります。これを行うには、articles.entity.ts
ファイルの ArticleEntity
クラスを次のように更新します。
これは、Prisma Client によって生成された Article
型の実装であり、各プロパティに @ApiProperty
デコレーターが追加されています。
次に、コントローラールートハンドラーに正しいレスポンス型を注釈する必要があります。NestJS にはこの目的のためのデコレーターセットがあります。
GET
、PATCH
、DELETE
エンドポイントには @ApiOkResponse
を、POST
エンドポイントには @ApiCreatedResponse
を追加しました。type
プロパティは、戻り値の型を指定するために使用されます。NestJS が提供するすべてのレスポンスデコレーターは、NestJS ドキュメントにあります。
これで、Swagger は API ページのすべてのエンドポイントのレスポンス型を適切に定義するはずです。
まとめと最後の注意
おめでとうございます!NestJS を使用して基本的な REST API を構築しました。このチュートリアルを通して、あなたは
- NestJS で REST API を構築しました
- NestJS プロジェクトに Prisma をスムーズに統合しました
- Swagger と OpenAPI を使用して REST API をドキュメント化しました
このチュートリアルの主なポイントの 1 つは、NestJS と Prisma で REST API を構築することがいかに簡単かということです。これは、構造化され、型安全で、保守性の高いバックエンドアプリケーションを迅速に構築するための非常に生産的なスタックです。
このプロジェクトのソースコードは GitHub で見つけることができます。問題に気づいた場合は、遠慮なくリポジトリに issue を作成するか、PR を送信してください。また、Twitter で直接私に連絡することもできます。
次の記事をお見逃しなく!
Prisma ニュースレターに登録する