0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

ExpressoTSで簡易APIサーバを作ってみる

Last updated at Posted at 2023-10-10

簡易APIサーバ構築については、ExpressJSを使用したサンプルコードをネット上で見つけることができます。
ExpressJSですと実装するときのコードは概ねJavaScriptベースになります。
私自身、TypeScriptのほうがなじみがあるので簡易APIサーバーを構築するフレームワークがないかと探していたところ、ExpressoTSというのがリリースされているこを見つけました。
今回は、ExpressoTSで簡易APIサーバを作ってみたのでその情報を紹介したいと思います。

ExpressoTS公式ページ

開発環境

  • OS: Windows10
  • node: 18.17.0
  • ExpressoTS: ver2.6.0
  • adapter-express: ver1.1.0

その他コマンドラインからビルドなどを実行する時はGitBashを使用します。標準で用意されるnpmコマンドラインにshellコマンドが含まれてるためです。

実装概要

  • ExpressoTSをnpmコマンドを使用し、globalの環境にインストールします。これで以下のexpressotsコマンドが使用できるようになります。
  • ExpressoTSのCLI expressotsコマンドを使用し、初期のプロジェクト(スケルトン)を自動生成します。
  • 初期生成されたコードからGetの動作確認をします。初期プロジェクトにhttp Getメソッドは実装済みのため。
  • つづいて、Postメソッドを実装します。ここでは、postリクエスト(Body部:Jsonデータ)の受取から、ビジネスロジック部分を呼び出し、シンプルなJSON形式のデータを返却するというものです。

ソースコード

今回検証したソースコードはこちらに置いていますので参考にしていただけると幸いです。

プロジェクトの準備

ExpressoTSインストール

$ npm i -g @expressots/cli

初期プロジェクト作成

$ expressots new simple-expressots-get-post

[🐎 Expressots]

? Project name (simple-expressots-get-post)  ※ エンターを押します
? Package manager (Use arrow keys)           ※ エンターを押します
> npm
  yarn
  pnpm
  bun
? Select a template (Use arrow keys)         ※ エンターを押します
> Non-Opinionated :: A simple ExpressoTS project.
  Opinionated :: A complete ExpressoTS project with an opinionated structure and features.
? Do you want to create this project? (Y/n)  ※ エンターを押します
※ プロジェクトの生成が開始されます。

Progress |████████████████████░░░░░░░░░░░░░░░░░░░░| 50% || Installing dependencies
      🐎 Project  simple-expressots-get-post created successfully!
      🤙 Run the following commands to start the project:
      $ cd simple-expressots-get-post
      $ npm run dev
      Happy coding!
      Please consider donating to support the project.
      💖 Sponsor: https://github.com/sponsors/expressots
※ ここまで表示されれば初期プロジェクトの生成の完了です。

生成されたディレクトリ構造

node_modules以外のディレクトリ内容は以下です。

$ tree -I node_modules
.
|-- README.md
|-- expressots.config.ts
|-- jest.config.ts
|-- package-lock.json
|-- package.json
|-- src
|   |-- app.container.ts
|   |-- app.controller.ts
|   |-- app.module.ts
|   |-- app.usecase.ts
|   `-- main.ts
|-- test
|   `-- app.usecase.spec.ts
|-- tsconfig.build.json
`-- tsconfig.json

生成された各ファイルの説明

これからGetとPostメソッドについて確認していきますが、その前に主要なファイルについて説明します。
今回のGetメソッドを確認する際に「app.controller.ts」と「app.usecase.ts」ファイルを主に参照します。

ファイル名 概要
app.container.ts アプリケーションの依存関係を記述
app.controller.ts HTTPリクエストを処理するためのコードを記述
app.module.ts アプリケーションの各部分を組み立て、結合を記述
app.usecase.ts アプリケーションのビジネスロジックを記述
main.ts アプリケーションのエントリーポイントとなるファイル

サービスの起動

サービスを起動します。

$ cd simple-expressots-get-post
$ npm run dev

> simple-expressots-get-post@1.0.0 dev
> tsnd ./src/main.ts

[INFO] 12:35:21 ts-node-dev ver. 2.0.0 (using ts-node ver. 10.9.1, typescript ver. 4.9.5)
Application version not provided is running on port 3000 - Environment: development
※ ここまで表示されれば正常に起動しています。3000ポートを使用します。

続いて、ソースコードの内容をみていきます。

Getメソッド

Getメソッドは、上記初期プロジェクトが生成されるとGetメソッドはすでに実装がされています。
実装されているソースコードを参照します。

続いて、リクエストをcurlコマンドにより送信します。
すでに実装されているGetメソッドの応答結果「Hello from ExpressoTS!」が得られます。

$ curl localhost:3000
Hello from ExpressoTS!
※ メッセージが帰ってくれば正常に動作しています。

Getメソッドの実装(入り部口部分)の確認

app.controller.tsにGetメソッドが実装されています。

import { BaseController } from "@expressots/core";
import { Response } from "express";
import { AppUseCase } from "./app.usecase";
import { Get, controller, response } from "@expressots/adapter-express";

/*
* AppController クラスの定義
* ExpressotsのBaseControllerクラスを継承しています。
* これにより、Expressotsの機能を利用できるようになります。
*/
@controller("/")
export class AppController extends BaseController {
    constructor(private appUseCase: AppUseCase) {
        super();
    }

    /*
    * @Get("/") デコレータは、GETリクエストを処理するためのメソッドを定義しています。
    * このメソッドは / ルートに対するGETリクエストを処理します。
    * この定義により「localhost:3000」でアクセスするとメッセージが返却されます。
    */
    @Get("/")
    execute(@response() res: Response) {
        return res.send(this.appUseCase.execute());
    }
}

Getメソッドの業務ロジックの確認

上記のthis.appUseCase.execute()の実装内容を以下に示します。
上記curlコマンドによりリクエストを送付した結果「Hello from ExpressoTS!」メッセージが返却されました。

import { provide } from "inversify-binding-decorators";

@provide(AppUseCase)
export class AppUseCase {
    /*
    * executeメソッドの戻り値に「Hello from ExpressoTS!」が設定されています。
    */
    execute() {
        return "Hello from ExpressoTS!";
    }
}

Getメソッドについて以上です。

Postメソッド

Postメソッドは、初期のプロジェクトに実装がされていないため、追加実装します。
なお、ExpressoTSは@Get("/") デコレータをPostに変更するだけでは動作せず、その他プロバイダなどを実装する必要があります。
数ファイル追加しつつ、既存のコードに追加記述しながら進めます。

Postメソッドのシナリオと実装概要

アクセスするURIパスは、http://localhost:3000/userとします。
リクエストにname,email項目を送信して、レスポンスにname,email,statusをセットし受け取ります。
POSTメソッドにおいて、JSON形式のリクエストBody部に名前と電子メールの項目、およびレスポンスには名前と電子メールに加え処理結果を返却する想定で実装をします。
なお、各ファイルを追加する際にexpressots generateコマンドを使用することができます。
これからの作業はexpressots generateコマンドを使用しながら進めます。

Postメソッド実装後のファイル群

初期で生成されたファイルの修正に加え、「app.provider.ts」および「user.dto.ts」を追加します。

$ tree -I node_modules
.
|-- README.md
|-- expressots.config.ts
|-- jest.config.ts
|-- package-lock.json
|-- package.json
|-- src
|   |-- app.container.ts
|   |-- app.controller.ts
|   |-- app.module.ts
+|   |-- app.provider.ts
|   |-- app.usecase.ts
|   |-- main.ts
+|   `-- user.dto.ts
|-- test
|   `-- app.usecase.spec.ts
|-- tsconfig.build.json
`-- tsconfig.json

Postメソッドの実装

以下に実装するファイルと詳細内容を説明します。

1. 送受信する項目の定義記述

user.dto.tsファイルを用意します。
このファイルにJSON形式で送受信を行う項目定義をします。
entityもしくはdtoとして定義します。今回は、dtoとして定義しました。
user.dtoを以下のコマンドにより追加します。

$ expressots generate dto user2
[🐎 Expressots]
  [dto]          user dto created! ✔️

続いて内容を編集します。
以下の内容を記述します。

export interface IUserRequestDTO {
  name: string;
  email: string;
}

export interface IUserResponseDTO {
  name: string;
  email: string;
  status: string;
}

2. POSTメソッドから呼び出される業務ロジック部分の記述

続いて、app.usecase.tsファイルを編集します。
このファイルは、すでにGetメソッドから呼び出されるexecute()メソッドが実装されています。
今回は、このファイルにPostメソッドで使用するexecutePostを追加で記述します。
ここで追加したexecutePostをControllerのPostデコーダ内の処理から呼び出します。

import { provide } from "inversify-binding-decorators";
import { IUserRequestDTO, IUserResponseDTO } from "./user.dto"

@provide(AppUseCase)
export class AppUseCase {
    execute() {
        return "Hello from ExpressoTS!";
    }

+    executePost(data: IUserRequestDTO): IUserResponseDTO {
+        return {
+            name: data.name,
+            email: data.email,
+            status: "Success"
+        };
+    }
}

3. POSTデコーダ部分の記述

app.controller.tsファイルに追加記述します。

import { BaseController, StatusCode } from "@expressots/core";
import { Response } from "express";
import { AppUseCase } from "./app.usecase";
import { Get, Post, body, controller, response } from "@expressots/adapter-express";
import { IUserRequestDTO, IUserResponseDTO } from "./user.dto"

@controller("/")
export class AppController extends BaseController {
    constructor(private appUseCase: AppUseCase) {
        super();
    }

    // こちらは自動生成されたGetメソッドの値の返却処理
    @Get("/")
    execute(@response() res: Response) {
        return res.send(this.appUseCase.execute());
    }

    /*
    * @Postを追加して、usreパスにアクセスします。
    * executePost()を呼び出し業務処理を行います。
    * 今回はリクエスト内容に処理結果を追加したJSONデータを返却する実装にします。
    * BaseControllerより継承されたcallUseCaseを呼び出しデータを返却し、処理を終了します。
    */
+    @Post("user")
+    async executePost(
+        @body() payload: IUserRequestDTO,
+        @response() res: Response
+    ): Promise<IUserResponseDTO> {
+        console.log("body: ", payload);
+        const result: IUserResponseDTO = this.appUseCase.executePost(payload);
+        return this.callUseCase(
+            result,
+            res,
+            StatusCode.Created,
+        );
+    }
}

4. Body部のパース処理の記述

app.provider.tsファイルを追加します。
以下の実装によりリクエストに含まれるBody部のデータを内部的に扱うことができるようになります。

$ expressots generate provider app
[🐎 Expressots]
  [provider]          app provider created! ✔️

app.provider.tsファイルを開き以下の内容を記述します。

/*
* @expressots/coreに定義されている、IMiddleware, Middlewareを読み込みます。
*/
import { provide } from "inversify-binding-decorators";
+ import { IMiddleware, Middleware } from "@expressots/core";
+ import { AppExpress } from "@expressots/adapter-express";
+ import { container } from "./app.container";

@provide(App)
export class App extends AppExpress {
+    private middleware: IMiddleware;
    /*
    * MiddlewareのaddBodyParserメソッドにてBody部のパースをするので、
    * 必要なモジュール類を用意します。
    */
+    constructor() {
+        super();
+        this.middleware = container.get<IMiddleware>(Middleware);
+    }
+    protected configureServices(): void {
+        this.middleware.addBodyParser();
+    }
}

5. Providerの有効化

このファイルが最後の編集ファイルです。

上記Providerを追加しましたので、AppFactoryに認識させます。
main.tsファイルを開きます。
上記で追加したapp.providerを認識させます。

import "reflect-metadata";

import { AppFactory } from "@expressots/core";
import { container } from "./app.container";
import { ServerEnvironment } from "@expressots/adapter-express";
+ import { App } from "./app.provider";

async function bootstrap() {
-    const app = await AppFactory.create(container, []);
+    const app = await AppFactory.create(container, App);
    await app.listen(3000, ServerEnvironment.Development);
}

bootstrap();

Postメソッドの動作確認

curlコマンドからPOSTとして、http://localhost:3000/userリクエスト送信が正しく動作するか確認します。
{"name":"aaa2","email":"aaa2@bbb","status":"Success"}が返却されれば正常に操作しています。

$ curl -X POST -H "Content-Type: application/json" \
-d '{"name": "aaa2", "email": "aaa2@bbb"}' \
http://localhost:3000/user
{"name":"aaa2","email":"aaa2@bbb","status":"Success"}

Postメソッドについては以上です。

補足

上記の初期プロジェクト作成時の手順でNon-OpinionatedOpinionatedとして生成すると、各種IOが含まるプロジェクトが生成されます。Non-Opinionatedでは基本的にはGetメソッドのみで、各資材やモジュールの配置もシンプルでした。
Opinionatedを指定して展開するとストレージ間I/O(参照、更新、削除処理)など各機能が満載なプロジェクトが生成されます。
興味のある方はお試しくださいませ。

? Select a template (Use arrow keys)         ※ 2つ目のOpinionatedを選択する
  Non-Opinionated :: A simple ExpressoTS project.
> Opinionated :: A complete ExpressoTS project with an opinionated structure and features.

さいごに

今回はExpressoTSで簡易APIサーバを作ってみるということで実装した内容を紹介しました。
次回は、ExpressoTSのバックエンドのなかでデータベースへの書き込み処理について実装してみたいと思います。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?