Magicode logo
Magicode
1
6 min read

[OpenAPI][Nuxt.js] APIクライアントを自動生成し、型安全にpluginsに組み込む

https://cdn.apollon.ai/media/notebox/6fcbd312-3eac-4ea0-826e-bb0088ec25c3.jpeg

概要

  • WebAPIの設計書としてOpenAPI Specification(以下、OpenAPI Spec)を採用する
    • フロントエンド、バックエンド共にOpenAPI Specからソースコードを自動生成する
    • 本記事はフロントエンドのみを対象とする
  • 自動生成したAPIクライアントをNuxt.jsのpluginsに組み込んでvueファイルから参照する
    • TypeScriptで型安全に開発する

前提条件

OpenAPI Spec

周辺ツールの対応状況を鑑みてv3.0.0を前提とする。

Docker

OpenAPI Specからソースコードを自動生成ツールとしてDocker版のOpenAPI Generatorを使用するため。 CLIやGradleプラグイン等あるが、グローバルな環境を汚さずにフロントエンド、バックエンド共に使用するツールを揃えられるのでDocker版を使用する。

Node.js / Yarn

フロントエンドの開発においてはもはやインストールされていて当たり前なものなので割愛。

Nuxt.js

Nuxt3は2022/8/4時点ではRelease CandidateなのでNuxt2を前提とする。

事前準備

プロジェクト作成

公式 の手順に従い、適当なプロジェクトを作成。
$ yarn create nuxt-app open-api-sample
yarn create v1.22.18
[1/4] 🔍  Resolving packages...
[2/4] 🚚  Fetching packages...
[3/4] 🔗  Linking dependencies...
[4/4] 🔨  Building fresh packages...

warning Your current version of Yarn is out of date. The latest version is "1.22.19", while you're on "1.22.18".
info To upgrade, run the following command:
$ curl --compressed -o- -L https://yarnpkg.com/install.sh | bash
success Installed "create-nuxt-app@4.0.0" with binaries:
      - create-nuxt-app

create-nuxt-app v4.0.0
✨  Generating Nuxt.js project in open-api-sample
? Project name: open-api-sample
? Programming language: TypeScript
? Package manager: Yarn
? UI framework: None
? Nuxt.js modules: Axios - Promise based HTTP client
? Linting tools: (Press <space> to select, <a> to toggle all, <i> to invert selection)
? Testing framework: None
? Rendering mode: Universal (SSR / SSG)
? Deployment target: Server (Node.js hosting)
? Development tools: (Press <space> to select, <a> to toggle all, <i> to invert selection)
? What is your GitHub username? i-zacky
? Version control system: None

🎉  Successfully created project open-api-sample

  To get started:

	cd open-api-sample
	yarn dev

  To build & start for production:

	cd open-api-sample
	yarn build
	yarn start


  For TypeScript users. 

  See : https://typescript.nuxtjs.org/cookbook/components/
✨  Done in 144.63s.

APIクライアントの自動生成

OpenAPI Specは 公式のサンプル を使用。
docker run --rm \
  -v "${PWD}/openapi:/out" \
  openapitools/openapi-generator-cli:v6.0.1 generate \
  --generator-name typescript-axios \
  --input-spec https://raw.githubusercontent.com/openapitools/openapi-generator/master/modules/openapi-generator/src/test/resources/3_0/petstore.yaml \
  --output /out \
  --api-package api \
  --model-package model \
  --generate-alias-as-model \
  --additional-properties supportsES6=true \
  --additional-properties withInterfaces=true \
  --additional-properties withSeparateModelsAndApi=true

本題

pluginsの実装

ポイントは2つ。
  • apiFactory でOpenAPI Generatorによって自動生成されたAPIクライアントを束ねていく
    • OpenAPI Specに追従して return ブロックを更新していく
    • 自動生成されたAPIクライアントにnuxt/axiosのAxiosインスタンスをインジェクションする
  • declare module で型定義をする
import { Context, Inject } from '@nuxt/types/app'
import * as openapi from '@/openapi/index'

const apiFactory = (context: Context) => {
  const config = () => {
    const basePath = process.env.API_BASE_URL
    return new openapi.Configuration({
      basePath,
    })
  }

  return {
    PetApi: new openapi.PetApi(config(), '', context.$axios),
    StoreApi: new openapi.StoreApi(config(), '', context.$axios),
    UserApi: new openapi.UserApi(config(), '', context.$axios),
  }
}

export default (context: Context, inject: Inject) => {
  inject('api', apiFactory(context))
}

declare module 'vue/types/vue' {
  interface Vue {
    $api: ReturnType<typeof apiFactory>
  }
}

pluginsの有効化

export default {
  // plugins以外は省略
  plugins: ['~/plugins/api']
}

コンポーネントから呼び出してみる

<template>
  <Tutorial/>
</template>

<script lang="ts">
import Vue from 'vue'

export default Vue.extend({
  name: 'IndexPage',
  methods: {
    async getPetById(id: number) {
      // this.$api.XXX.YYYでOpenAPI Generatorで自動生成したAPIクライアントを呼び出せる
      await this.$api.PetApi.getPetById(id)
    }
  }
})
</script>

Discussion

コメントにはログインが必要です。