.../articles/

OpenAPI Generator + TypeScript で始める自動生成の型に守られた豊かなクライアント生活

OpenAPIをドキュメントだけでなく、Schema firstな開発に利用しようということで、TypeScript + OpenAPI Generatorでフロントエンドの開発をしました。 Vue, Nuxtで使う際の例と共にその内容を紹介します。

目次

  1. OpenAPIでドキュメントを書くメリットなど
  2. OpenAPI GeneratorでClient情報を吐き出す
  3. Vue, Nuxtで利用する
  4. APIの変更に追従しやすく、型もあるし幸せ

OpenAPIでドキュメントを書くメリットなど

弊社では以下のブログでも書いているように、API仕様のドキュメント化にOpenAPIを活用しています。

フロントエンドとバックエンドで実装する人が異なる場合、また、将来的に異なる人が実装する可能性がある場合や、単純に見やすいドキュメント作りという点で考えても、OpenAPIを利用するメリットは大きいと感じております。
そして、そのメリットを拡大してくれるなと感じているのが、OpenAPI Generatorを利用した開発です。
弊社ではフロントエンドの実装はNuxt.js + TypeScriptを利用することが多いのですが、そこでaxiosなどを利用してAPIをリクエストする部分などにOpenAPI Generatorで吐き出した型を利用すると、

  • 型に関するファイルを自分で書かなくて済む
  • 自分で書かないので間違いが発生しにくい(元々のファイルが間違っていたらダメなんですが)
  • APIの修正に追従しやすい

などのメリットが増えました。
今回はOpenAPI GeneratorのVue.js, Nuxt.jsへの導入方法などをご紹介したいと思います。


1. OpenAPI GeneratorでClient情報を吐き出す

1.1 packageのインストール

yarnやbrewでglobalにinstallする方法もあるのですが、CIなどでチェックする場合にも使いたいので、私はyarnでprojectごとにopenapi-generatorをinstallしています。

$ yarn add -D @openapitools/openapi-generator-cli

or

$ npm install @openapitools/openapi-generator-cli -D

1.2 package.jsonのscriptsに追記

Clientを生成するscript generate-client などを追記します。

openapi-generator generate -g typescript-axios

-g で利用するGeneratorを選択します。
今回は typescript-axios を利用していますが、利用する言語・フレームワークによって変更可能です。(参考 *2)

<小ネタ>
check-yml format-yml validate-schema なども合わせて記述していますが、
prettierを利用したyamlのフォーマットチェックやgeneratorのvalidateはCIでチェックする際に便利なのでおすすめです😎

{
  "name": "Application-Name",
  "version": "1.0.0",
  "private": true,
  "scripts": {
    "check-yml": "prettier --check './path/to/openapi.yaml'",
    "format-yml": "prettier --write './path/to/openapi.yaml'",
    "validate-schema": "openapi-generator validate -i ./path/to/openapi.yaml",
    "generate-client": "openapi-generator generate -g typescript-axios -i ./path/to/openapi.yaml -o ./frontoend-application-directory/src/types/typescript-axios",
  },
  "devDependencies": {
    "@openapitools/openapi-generator-cli": "^1.0.10-4.2.3",
  }
}

1.3 Clientの生成

先ほど登録したscriptでclientを生成します。

$ yarn generate-client

生成後のソースは以下のようになります。
これでClient生成までは完了で準備が終わった状態です。

$  tree -L 2 ./types
./types
├── client-axios
│   ├── api.ts
│   ├── base.ts
│   ├── configuration.ts
│   ├── git_push.sh
│   └── index.ts
└── index.d.ts

2. Vue, Nuxtで利用する

ここからは先ほど生成したClientをどのように利用しているかの話しです。
生成された型情報などを、どのように使っていくのかをログイン画面のソースをベースに説明します。

2.1 Yamlと生成されたclient

以下は api.ts に含まれる ログイン時に必要な loginId, passwordの情報です。
もともとのOpenAPIの情報が記述されたyamlの内容をgenerateすると api.ts の内容が生成されます。

openapi.yaml
からLoginRequest部分を抜粋。

    LoginRequest:
      title: LoginRequest
      type: object
      description: 認証情報
      required: [loginId, password]
      properties:
        loginId:
          type: string
          description: ログインID
        password:
          type: string
          description: パスワード

ファイル: ./types/typescript-client/api.ts

/**
 * 認証情報
 * @export
 * @interface LoginRequest
 */
export interface LoginRequest {
    /**
     * ログインID
     * @type {string}
     * @memberof Auth
     */
    loginId: string;
    /**
     * パスワード
     * @type {string}
     * @memberof Auth
     */
    password: string;
}

2.2 生成されたClientをVueから利用する

上で生成されたClient情報を、login.vueで読み込みます。
data の interfaceで生成された型の情報を利用しています。
普段だとこの情報を自分で記述する必要があると思いますが、
生成された内容を利用するだけになるので楽ができますね🤗

ファイル: ./pages/login.vue

import Vue from 'vue'
import Cookies from 'universal-cookie'
import { LoginRequest } from '@/types/typescript-axios'

interface Data {
  value: LoginRequest,
}

export default Vue.extend({
  components: {
  },
  data: (): Data => {
    return {
      value: {
        loginId: '',
        password: '',
      },
    }
  },
  methods: {
    async login() {
      try {
        await this.$store.dispatch('auth/login', {
          basePath: process.env.VUE_APP_BASE_PATH, // APIのURL
          value: this.value,
          cookie: new Cookies(),
          stage: process.env.VUE_APP_STAGE, // localでcookieにSecure属性をつけないようするために渡しているだけです(不要な方は消してOK)
        })
      } catch (error) {
        // エラーをよしなに
      }
    },
  },
})

2.3 生成されたClientをstore(Vuex)で利用する

login.vue の loginメソッドで呼ばれているstore/authの内容です。
こちらでは、 LoginRequest の型情報に加えて、 AuthApi のclassもimportします。
storeの中でこれを利用してAPIへのリクエストを行っています。

生成された AuthApi の方でaxiosを利用してリクエストする部分が記述してあるため、storeではエンドポイントに対応したメソッドを呼んでやるだけになります(便利ポイントですね!)

AuthApi({ basePath: params.basePath })

と、APIのURLを渡していますが、認証を求められるエンドポイントなどは以下のようにtokenを渡してやると、リクエスト時に Bearer など指定の認証方法に応じてHeaderに追加してくれます。

new MeApi({
basePath: params.basePath,
accessToken: params.accessToken,
})

ファイル: ./store/auth.ts

import { Module, ActionTree, GetterTree, MutationTree } from 'vuex'
import { LoginRequest, AuthApi } from '@/types/typescript-axios'
import Cookies, { CookieSetOptions } from 'universal-cookie'
import { RootState } from '@/types/index'

interface AuthState {
}

interface LoginParams {
  basePath?: string,
  value: LoginRequest,
  cookie: Cookies,
  stage: string,
}

export const state = (): AuthState => ({
})

export const getters: GetterTree<AuthState, RootState> = {
}

export const mutations: MutationTree<AuthState> = {
}

export const actions: ActionTree<AuthState, RootState> = {
  async login(_ctx, params: LoginParams) {
    try {
      const { data } = await(new AuthApi({ basePath: params.basePath }))
        .postAuthLogin(params.value)
      params.cookie.set(
        'accessToken',
        data.accessToken,
        {
          secure: params.stage !== 'local',
          maxAge: data.expiresIn,
        } as CookieSetOptions,
      )
    } catch (error) {
      throw error.response
    }
  },
}

export const auth: Module<AuthState, RootState> = {
  namespaced: true,
  getters,
  mutations,
  actions,
}

3. APIの変更に追従しやすく、型もあるし幸せ

上記のようにOpenAPIから型の情報を生成し、フロントエンドで利用する流れを実装してみました。

APIの変更を把握し追従するのは、フロントエンドの組み込みをする上でなかなか骨の折れる部分かもしれませんが、Generatorを利用することでビルドエラーなどで修正するポイントが把握できるので事故りにくい状態が作れるかと思います。

あとは、フロントエンドのテストにうまく組み込みたいなと思っているところですが、そこがまだ出来ていないので、今後の課題とさせていただきます。


参考

*1 https://openapi-generator.tech/
*2 OpenAPI generatorを試してみる
*3 https://github.com/OpenAPITools/openapi-generator

.../articles/

Articles

記事

AWS AmplifyにmonorepoのNext.js(App Router)をデプロイする

AWS AmplifyにmonorepoのNext.js(App Router)をデプロイする

monorepo管理しているNext.jsをAmplifyにデプロイしようとした際にいくつか躓く内容があったのでまとめておきます。

リモートワーク・オンライン会議でも、スムーズに制作を進めるために大切なこと[資料編]

リモートワーク・オンライン会議でも、スムーズに制作を進めるために大切なこと[資料編]

コロナ禍の影響により、リモートワークの導入をおこなっている制作会社も多く、実際に弊社でも導入しています。

売れるECサイトデザインを作るために。参考にしたいおしゃれな事例の探し方。

売れるECサイトデザインを作るために。参考にしたいおしゃれな事例の探し方。

売れるECサイトのデザインは、「この形式」という決まりはありません。ECサイトで売り上げを上げるなら、しっかりとしたコンセプトと、コンセプトを決定するまでのリサーチが必要です。

制作会社の考える、業務効率化ツールのおすすめ。個人でも使いやすいサービスなど。

制作会社の考える、業務効率化ツールのおすすめ。個人でも使いやすいサービスなど。

新型コロナウイルス感染拡大の影響で、リモートワークが主流になり、弊社でも週のほとんどは各自宅で作業をしています。

Figmaでデザインのコミット履歴を残せるプラグイン【Thought Recorder】をリリースしました

Figmaでデザインのコミット履歴を残せるプラグイン【Thought Recorder】をリリースしました

Figmaを利用するWebデザイナーの助けになれると嬉しいです。使い方は本記事をご覧ください。

ECの構築方法、おすすめのECサービス。

ECの構築方法、おすすめのECサービス。

ファッションや家電、スーパーの買い物でさえもECサイトを利用することが当たり前になりました。加えて新型コロナウイルスの影響もあり、弊社にも「どんなプラットフォームを利用したら良いか」「どれくらいコストがかかるのか」などECに関するさまざまなご相談を頂きます。

FastAPIのスキーマクラスをOpenAPIから生成する方法

FastAPIのスキーマクラスをOpenAPIから生成する方法

PythonでAPIを構築する要件があり、フレームワークに比較的モダンなFastAPIを採用しました。FastAPIはバックエンドの開発を行えば自動でOepnApi定義を生成する機能が備わっていますが、今回はこれを使わず、事前に用意したOepnApi定義からFastAPIで利用するスキーマクラスを生成する方法を紹介します。

Laravel 日本一解りやすい全文検索のマイグレーション記載方法解説

Laravel 日本一解りやすい全文検索のマイグレーション記載方法解説

Laravel + MySQLで全文検索を実装する

GiFT1号目新卒デザイナーの2021年振り返り

GiFT1号目新卒デザイナーの2021年振り返り

いつの間に、年末ですね。入社してもう、9ヶ月も立っていたようです。2021年の振り返りを記事にしました。

TimesclaeDBのデータ圧縮に関して

TimesclaeDBのデータ圧縮に関して

TimescaleDBはデータベース内の一部のテーブルを時系列データとして扱えるPostgreSQLの拡張です。PostgreSQLの機能拡張なので非常に手軽に導入できます。今回はこのTimesaceDBの圧縮について調べたので備忘録として書き綴りました。

すべての記事

お問い合わせ