npm workspace で TypeScript 環境の素朴なモノレポを作るには

#雑記 #備忘録

というテーマで素朴なやつを作った

https://github.com/yoshikouki/monorepo-sandbox


条件は以下。おそらく条件は満たせている・・・と思う

  • apps 配下の各プロジェクトは、packages/db を利用する

  • apps 配下の各プロジェクトは、packages/db を TypeScript のままインポートしたい

  • apps 配下の各プロジェクトのビルド時は、packages/db も一緒にビルドされる


プロジェクト構成

tree --gitignore .
.
├── Dockerfile
├── apps
│   └── api
│       ├── package.json
│       ├── src
│       │   └── index.ts
│       └── tsconfig.json
├── docker-compose.yaml
├── package-lock.json
├── package.json
├── packages
│   └── db
│       ├── package.json
│       ├── prisma
│       │   ├── migrations
│       │   │   ├── 20240613112605_init
│       │   │   │   └── migration.sql
│       │   │   └── migration_lock.toml
│       │   └── schema.prisma
│       ├── src
│       │   └── index.ts
│       └── tsconfig.json
└── tsconfig.json


packages/db に Prisma のようなDBClientを配置しておいて

import { PrismaClient } from '@prisma/client'

export const db = new PrismaClient()


apps/api から import する。Next.js などで作った apps/web のような別プロジェクトからも利用できることを想定している。今回はスコープ機能 "@scope/db" は使わなかった

import { db } from 'db';
import express from "express";

const app = express();
const port = 3333;

app.get("/", async (req, res) => {
  const id = crypto.randomUUID()
  await db.user.create({
    data: {
      email: `${id}@example.com`,
      name: `User ${id}`,
    },
  })
  const allUsers = await db.user.findMany()
  console.log(allUsers.length)
	res.send(`Users count: ${allUsers.length}`);
});

app.listen(port, () => {
	console.log(`Example app listening on port ${port}`);
});
アクセスする度にユーザーが増えるだけのAPI


設定

root 

./package.json

{
  "name": "monorepo-sandbox",
  "version": "1.0.0",
  "private": true,
  "scripts": {
    "build": "npm run build -w api"
  },
  "workspaces": [
    "packages/db",
    "apps/api"
  ],
  "devDependencies": {
    "@types/node": "^20.14.2",
    "ts-node": "^10.9.2",
    "typescript": "^5.4.5"
  }
}


packages/db

packages/db/package.json

{
  "name": "db",
  "version": "1.0.0",
  "main": "dist/index.js",
  "types": "dist/index.d.ts",
  "scripts": {
    "build": "tsc --build"
  },
  "devDependencies": {
    "prisma": "^5.15.0"
  },
  "dependencies": {
    "@prisma/client": "^5.15.0"
  }
}

main と types が重要。ビルド後に参照するファイルを指している


packages/db/tsconfig.json

{
  "compilerOptions": {
    "outDir": "dist",
    "rootDir": "src",
    "composite": true,
    "declaration": true,
    "declarationMap": true,
    "esModuleInterop": true,
    "skipLibCheck": true
  },
  "include": ["src"],
  "exclude": ["node_modules", "dist"]
}


apps/api

apps/api/package.json

{
  "name": "api",
  "version": "1.0.0",
  "main": "index.js",
  "scripts": {
    "build": "tsc --build"
  },
  "dependencies": {
    "express": "^4.19.2"
  },
  "devDependencies": {
    "@types/express": "^4.17.21"
  }
}

依存関係に "db" が存在しないことに注目


apps/api/tsconfig.json

{
  "compilerOptions": {
    "outDir": "dist",
    "rootDir": "src",
    "composite": true,
    "declaration": true,
    "declarationMap": true,
    "esModuleInterop": true,
    "skipLibCheck": true
  },
  "include": ["src"],
  "exclude": ["node_modules", "dist"],
  "references": [
    { "path": "../../packages/db" }
  ]
}


おわりに

おそらく最小構成になっているはずだ


この記事が気に入ったらサポートをしてみませんか?