なぽろぐ

気ままに感じたことを記事にまとめます。Vtuberのイベントのことと、プログラム関連のことが多めだと思います。

aspidaとNestJSでHTTPClientにも型定義しながら開発するぞ!!!!!!!

aspidaとNestJSを使って気持ちがいい開発がしたい!したくない?

今回もお試しリポジトリが存在します.NestJS x aspida x Reactの環境で作っています.是非お試しください

github.com

なにするの?

NestJSはにはSwaggerModuleがありOpenAPIでしゃべることが可能になります.また, aspidaにはopenapi2aspidaというサブライブラリが存在します.この2つの組み合わせによりNestJS -> OpenAPI -> aspidaができるようになりNestJSでControllerを書くと自動的にHTTPClientの型定義が生成されるという感じになります.バックエンドを書くとフロントエンドに必要なコードが生成されるのは気持ちがいいですね.

OpenAPI経由でaspidaの設定ファイルが吐かれるのでNestJSに限らずいろいろは場面で使えるかと思います.今回はその一例としてNestJSを利用しています.

aspidaとは

github.com

HTTPClientに型をつけてくれるライブラリです.GETクエリ, Postパラメータはもちろん, Formデータにも型がつくらしい. しかもURIも保管してくれたりします.

使用感はこんな感じです

今回は /api/user/に問い合わせるとUser オブジェクトが返ってくるようなapiを作りました.

Image from Gyazo

導入方法

yarn add @aspida/axios axios

初期設定だと<root>/apis/以下にエンドポイントと同じ名前のディレクトリを切って設定ファイルを置いていくことになります.この設定ファイルを読んで<root>/apis/$api.tsが作成されます.

aspidaのビルド設定ファイルは公式を見てください aspida/packages/aspida at master · aspidajs/aspida · GitHub

例えば/api/userというエンドポイントが生えていたとすると,次のようにファイルを作って設定を書けばおkです

export interface Methods {
  get: {
    resBody: Types.User
  }
}

作成後に

yarn aspida --build

<root>/apis/$api.tsが作られ型定義されたHTTPClientを使用することができます.

aspidaの設定ファイルは必ずHTTPクライアントをラップする必要があるので,実際使用するときはこのようなutil関数を作っておくと楽だと思います.

import client from "apis/$api";
import aspida from "@aspida/axios";

export const api = client(aspida());

openapi2aspida

OpenAPI形式のjsonをHTTP通信形式で受け取り,aspidaの設定ファイルを自動で作ってくれます.めちゃくちゃ便利.

ルートにaspida.config.jsonを作っておき

module.exports = {
  input: "apis", // outputディレクトリ,
  openapi: { inputFile: "http://localhost:3000/swagger-json" }
}

でおkです.サーバを起動した状態で, openapi2aspida --buildでaspidaの設定ファイルが全て自動生成されます.便利すぎる・・・・

2回目移行ビルドするときはoutputディレクトリ丸ごと消しておく必要があります.

fatal: destination path 'apis' already exists and is not an empty directory.

NestJSについて

Node.jsにおいてバックエンドサーバといえば, Express一強みたいなところがありましたが,最近伸びているバックエンドフレームワークです.Angulerに近い書き心地で様々な機能を後からバシバシ入れることができるプログレッシブフレームワークになっています.Star数も22.5kとかなり多くなってきていて今注目のフレームワークって感じがしてきますね

github.com

今回はNestJSには深く触れず, SwaggerModuleを入れるところから始めていきたいと思います.

2019NestJSのアドベントカレンダーに少し参加したので興味のある人はこちらへ

SwaggerModuleを入れてOpenAPIを喋れるようにする

公式ドキュメントにSwaggerModuleの導入の仕方が書いてあるのでそちらを参考に進めていきます.

docs.nestjs.com

必要なモジュールを入れる

yarn add @nestjs/swagger swagger-ui-express

main.tsを次のように書き換える

import { NestFactory } from "@nestjs/core";
import { SwaggerModule, DocumentBuilder } from "@nestjs/swagger";
import { AppModule } from "./app.module";

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.setGlobalPrefix("api"); // frontendのrenderを/においているのでNestJSで定義される全てのControllerをapi/xxxにしておく

  // 公式とほとんど同じ
  const options = new DocumentBuilder()
    .setTitle("API description")
    .setVersion("1.0")
    .addServer("http://localhost:3000/")
    .build();

  const document = SwaggerModule.createDocument(app, options);
  SwaggerModule.setup("swagger", app, document);

  await app.listen(3000);
}
bootstrap();

ここでaddServerをやっておかないとopenapi2aspidaが実行できないので気をつけましょう.現在これは修正されています

この行で落ちます

 const options = new DocumentBuilder()
    .setTitle("API description")
    .setVersion("1.0")
    .addServer("http://localhost:3000/")
    .build();

これだけでNestJSがOpenAPIを喋れるようになりました.

yarn start:dev

をやってから

特に何もしていなければhttp://localhost:3000/swaggerでこのような画面を見れると思います. Image from Gyazo

また,http://localhost:3000/swagger-jsonでOpenAPIを喋っているのがJSON形式でわかります

なんの型が返ってくるかを明示するためにapp.controller.tsを次のように書き換えます

import { Controller, Get, Post, Body } from "@nestjs/common";
import { AppService } from "./app.service";
import { ApiTags, ApiCreatedResponse } from "@nestjs/swagger";

@ApiTags("AppController")
@Controller()
export class AppController {
  constructor(private readonly appService: AppService) {}

  @Get()
  @ApiCreatedResponse({ type: String }) // ここでstringが返ってくることを書いておく
  getHello(): string {
    return this.appService.getHello();
  }
}

これでopenapi2aspidaで型を自動生成する準備は万端です.実際にビルドして使用感を確かめてみましょう

NestJSから吐かれるOpenAPIからaspidaの設定ファイルを生成してみる

NestJSを次のコマンドで起動します

yarn start:dev

http://localhost:3000/swagger-jsonが生きてることを確認し,aspida.config.jsに以下の設定が書いてあれば準備はおkです

module.exports = {
  input: "apis",
  openapi: { inputFile: "http://localhost:3000/swagger-json" }
}

このコマンドを実行すれば自動生成されます

rm -rf apis
yarn openapi2aspida --build

たったこれだけで<root>/apis/$api.tsが生成されました. f:id:Naporitan:20200329084534p:plain

すごいですよね!!! あとはこれをフロントエンドのコードで使うだけ!OpenAPIをメンテしていく意味も出てくるし一石二鳥のライブラリだと思います.

まとめ

去年の12月ごろにも一度使用していたのですが,かなりのアップデートがかかっていてびっくりしました. naporitan.hatenablog.com

NestJSとaspidaを使えばすべてTSの世界でwebの世界を渡り歩けるようになりました.今実戦投入するプロジェクトにこの構成をとっていますが,いい感じです.

副作用として,バックエンドでAPIを書き換えるとき影響範囲がTSの型チェックで落ちたところを修正すればよくなるのでAPI変更の際の精神負荷も少し軽減される気もします.

バックエンドでAPIを作成するとすぐ型安全な状態でそのAPIにアクセスできると言うのは圧巻だと思います. aspida流行れ・・・・!!!