🚀

aspida の API 定義をパッケージ化する

5 min read

自分用備忘録 兼 誰かのための記事

tsconfig をいい感じに設定して、npm に公開する記事です。

なんでパッケージ化?

フロントエンドが 2 つに分かれたりすると、aspida の API 定義も 2 箇所に分かれてしまいます。なら共有できるようにパッケージ化しようということで

サンプル

というよりこないだパッケージ化したリポジトリ(以前は swagger がここにありました)

afes-website/docs

ディレクトリ構成

docs/
  ├ .github/
  │   └ workflows/
  │       └ publish.yml
  ├ tsconfig.json
  └ src/
      ├ api/    ← いつもの
      └ index.ts

ファイルの中身

それぞれ最低限の要素 + α 程度のみ

package.json

package.json
{
  "name": "@afes-website/docs",
  "version": "2.2.11",
  "description": "73rd Afes website API",
  "repository": {
    "type": "git",
    "url": "https://github.com/afes-website/docs.git"
  },
  "main": "dist/index.js",
  "files": [
    "dist",
    "package.json"
  ],
  "scripts": {
    "api:build": "aspida",
    "build": "aspida --build && tsc --build tsconfig.json"
  },
  "license": "MIT",
  "peerDependencies": {
    "aspida": "^0.21.0",
    "typescript": "^4.0.2"
  },
  "devDependencies": {
    ...
  }
}

tsconfig.json

tsconfig.json
{
  "compilerOptions": {
    "rootDir": "./src",
    "baseUrl": "./src",
    "outDir": "./dist",
    "declaration": true,
    "target": "es6",
    "lib": [
      "esnext",
      "dom"
    ],
    "skipLibCheck": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "esModuleInterop": true,
    "module": "commonjs",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true
  },
  "include": [
    "**/*.ts"
  ],
  "exclude": [
    "node_modules",
    "dist"
  ]
}

src/index.ts

@types 系を全部 export しておくと import { hoge } from "@afes-website/docs" ができて便利

src/index.ts
// apis
export { default as api } from "./apis/$api";
export { default } from "./apis/$api";

// types
export * from "./apis/@types";
export * from "./apis/auth/@types";
export * from "./apis/blog/articles/@types";
export * from "./apis/blog/revisions/@types";
export * from "./apis/blog/categories/@types";
export * from "./apis/blog/revisions/contrib/@types";
export * from "./apis/online/exhibition/@types";
export * from "./apis/online/drafts/@types";
export * from "./apis/onsite/exhibition/status/@types";
export * from "./apis/onsite/general/term/@types";
export * from "./apis/onsite/general/guest/@types";
export * from "./apis/onsite/general/guest/_id@string/log/@types";
export * from "./apis/onsite/reservation/@types";

// utils
export * from "./apis/images/@utils";

余談: 一番下の utils はエンドポイントからパスを返す関数が入っています。今の aspida では $path が実装されているので要らないですね………

公開する

.github/workflows/publish.yml
name: publish as package

on:
  push:
    tags:
      - "v*"

jobs:
  publish:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - name: Cache yarn packages
        uses: actions/cache@v1
        with:
          path: ~/.cache/yarn
          key: ${{ runner.os }}-yarn-${{ hashFiles(format('{0}{1}', github.workspace, '/yarn.lock')) }}
          restore-keys:
            ${{ runner.os }}-yarn-
      - name: install packages
        run: yarn install
      - name: build
        run: yarn build
      - name: prepare npm config
        run: npm config set //registry.npmjs.org/:_authToken=$NPM_TOKEN
        env:
          NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
      - name: publish
        run: npm publish --access public

tag push をトリガーとして npm に公開しています。もう少しいい方法があると思いつつ、動くのでヨシ!しています………

つかう

Terminal
$ yarn add @afes-website/docs@~2.2
package.json
  "devDependencies": {
    "@afes-website/docs": "~2.2",
    ...
  }
hoge.ts
import api, { UserInfo } from "@afes-website/docs";

おしまい

こここうしたほうがいいよ!とかあれば教えて下さい!

世界中のWebAPIの型定義をnpmに登録出来たらもっとフロントが楽しくなる
内製のAPIは上記のように一人が型定義をしておけば他のメンバーが安心してHTTPリクエストを行えるようになります
一方でTwitter, GoogleMap, YouTubeなど一般公開されててよく使うAPIは、型定義を誰かが書いてnpmに登録しておいてくれたら世界中のみんながラクにAPIを使えるようになりそうじゃないですか!?
ということで@types/nodeのノリで@aspida/twitterでTwitter APIの型定義をインストールできるようになるDefinitelyTypedみたいな仕組みも絶賛開発中です
HTTPリクエストを型安全にする手法とNuxt TSでの実装例 - Qiita

これの実装を心待ちにしています

Special Thanks: Solufa, ibuki2003