【TypeScript】Express使用時にroutesディレクトリ以下を自動で読み込む方法

■はじめに

皆さんは楽しいtypescriptライフを送っていますか?
フロトエンド界隈でデファクトスタンダードになりつつある言語ですが、
今回はサーバーサイド、ExpressでAPIサーバーを作るという前提で
表題の機能を実装していきます。

■よく紹介されている書き方

なぜ自動で読み込めたら楽だなーという発想になったのか、
いろんな技術系サイトでコード例として紹介される書き方をご覧ください。

import {Express} from "express"
import { users } from "./routes/users"
import { stores } from "./routes/stores"
import { roles } from "./routes/roles"

const router = Express.Router()

router.use("/users", users)
router.use("/stores", stores)
router.use("/roles", roles)

export = router

これ、ルーティング先増やすたびにimportとuse追加するのダルくないですか?
というわけで、routes/のファイル一覧を取得して自動で
import、useする機能を作成します。

■自動ルーティング

まずはプログラムを掲載します。

routes.ts

import Express from "express"
import { readdirSync } from "fs"
import { baseName } from "../utils"

const fileList = readdirSync("./src/routes", { withFileTypes: true })
  .filter((dirent) => dirent.isFile())
  .map((dirent) => dirent.name)

const router = Express.Router()

for (const file of fileList) {
  console.log(baseName(file))
  // tslint:disable-next-line:no-var-requires
  router.use(`/${baseName(file)}`, require(`./routes/${baseName(file)}`))
}

export = router

utils.ts

export function baseName(name: string): string {
  let base = name.substring(name.lastIndexOf("/") + 1)
  if (base.lastIndexOf(".") !== -1) {
    base = base.substring(0, base.lastIndexOf("."))
  }

  return base
}

やってることはいたってシンプルです。

  1. ディレクトリの中からファイル名のみを取得
  2. 拡張子を取り覗いてrouter.useにURIとrequire先を指定

(utils.tsは拡張子を取り除くための関数が入っています。
実装が面倒だったのでコピペしました。すいません!)

require文はそのまま使うと暗黙的にany型になってしまい、
tslintに怒られてしまうのでコメントで一時的に回避しています。

■最後に

まだまだtypescirpt初心者ですがこれでルーティングが楽になりそうです。
このコードではディレクトリ内のファイル全て読み込むことになっているので
拡張子チェックなどを入れるといいと思います。

個人的にはやはり、requireの部分が気持ち悪いので何とかしたいのですが
any型に溺れてしまいました。

意見・質問があればぜひ教えてください!