npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@taozizzz/tenant-db-router

v0.1.0

Published

Internal multi-tenant database routing package

Readme

@company/db-tenant

内部多租户数据库路由包,把“选库、隔离、连接管理”收敛成平台能力。

适用于「控制库(控制面) + 租户库(数据面)」的架构:请求进来后先确定 tenantId,再按租户元信息路由到对应数据库连接。

能力概览

  • 使用 AsyncLocalStorage 管理 TenantContext
  • TenantRegistry 提供租户元信息查询与缓存
  • PrismaFactory 管理 Prisma Client 缓存、空闲回收、指纹校验
  • TenantDb 统一入口:control() / tenant()

安装

# npm
npm i @company/db-tenant

# pnpm
pnpm add @company/db-tenant

说明:

  • 本包导出的是通用的「租户路由/缓存/连接池」能力,不强绑定 ORM;示例使用 Prisma。
  • 构建产物为 CommonJS:dist/index.js(详见 package.json)。

关键概念

  • TenantContext:一次请求/任务的上下文(至少包含 tenantId),通过 AsyncLocalStorage 透传。
  • TenantMeta:租户元信息(status / dbConfig / 可选 fingerprint 等),一般来自控制库。
  • 控制库(Control DB):存租户元信息的“控制面”数据库。
  • 租户库(Tenant DB):每个租户自己的“数据面”数据库(可能是一库一租户/一 schema 一租户等)。

快速开始(示例)

import { TenantDb, TenantRegistry, PrismaFactory, tenantContext } from '@company/db-tenant'
import { TenantFingerprintMismatchError } from '@company/db-tenant'
import { PrismaClient } from '@prisma/client'

const controlClient = new PrismaClient({
  datasources: { db: { url: process.env.CONTROL_DB_URL } }
})

// 1) 实现租户元信息查询(通常来自控制库)
const registry = new TenantRegistry({
  async getTenantMeta(tenantId) {
    return controlClient.tenant.findUnique({
      where: { tenantId },
      select: {
        tenantId: true,
        status: true,
        fingerprint: true,
        dbUrl: true
      }
    }).then((row) =>
      row
        ? {
            tenantId: row.tenantId,
            status: row.status,
            fingerprint: row.fingerprint,
            dbConfig: { url: row.dbUrl }
          }
        : null
    )
  }
})

// 2) 管理租户 PrismaClient:缓存 / 最大数量 / 空闲回收 / 指纹校验(可选)
const factory = new PrismaFactory({
  createClient: (databaseUrl, meta) =>
    new PrismaClient({
      datasources: { db: { url: databaseUrl } }
    }),
  validateFingerprint: async (client, meta) => {
    const record = await client.tenantFingerprint.findFirst({
      select: { fingerprint: true }
    })
    if (!record || record.fingerprint !== meta.fingerprint) {
      // 抛出该错误会原样透传;抛其他错误会被包装成 TenantFingerprintMismatchError
      throw new TenantFingerprintMismatchError(meta.tenantId)
    }
  },
  // maxClients: 2000,
  // idleTtlMs: 10 * 60 * 1000,
})

const db = new TenantDb(registry, factory, { controlClient })

// 3) 请求入口绑定租户上下文(以 Express/Koa 风格中间件为例)
function withTenantContext(req, res, next) {
  const ctx = {
    tenantId: req.user.tenantId,
    userId: req.user.userId,
    requestId: req.id
  }
  return tenantContext.run(ctx, next)
}

// 4) 业务层访问
async function handler() {
  const tenantDb = await db.tenant()
  return tenantDb.user.findMany()
}

API 速查

tenantContext

  • tenantContext.run(context, fn):在 fn 执行链路内绑定上下文。
  • tenantContext.with(patch, fn):在当前上下文基础上叠加字段(常用于补充 requestId 等)。
  • tenantContext.get():读取当前上下文(可能为空)。
  • tenantContext.require():读取并校验 tenantId;缺失时抛 TenantContextMissingError

TenantDb

  • db.control():返回控制库 client(透传你传入的 controlClient)。
  • await db.tenant():按当前上下文的 tenantId 路由并返回租户 client。

可选项(new TenantDb(..., options)):

  • allowedStatuses:允许的租户状态,默认 ['ACTIVE']
  • contextAccessor:自定义上下文访问器(默认使用 tenantContext)。

TenantRegistry

职责:tenantId -> TenantMeta,带缓存与并发去重。

常用方法:

  • await registry.getTenantMeta(tenantId):获取租户元信息(缓存命中则直接返回)
  • registry.invalidate(tenantId):租户信息变更后主动失效
  • registry.clear():清空缓存

可选项(new TenantRegistry(adapter, options)):

  • ttlMs:缓存 TTL,默认 5 分钟
  • maxSize:缓存最大条数,默认 1000
  • useStaleOnError:控制库/adapter 异常时是否用过期值兜底,默认 true
  • logger:可选日志接口

PrismaFactory

职责:tenantId -> PrismaClient 缓存,支持空闲回收、最大 client 数量控制、指纹校验。

常用方法:

  • await factory.getClient(tenantId, meta):获取/创建租户 client
  • await factory.closeClient(tenantId):关闭并移除单个租户 client
  • await factory.closeAll():关闭并移除全部 client(用于进程退出/优雅停机)
  • await factory.evictIdleClients():按 idleTtlMs 清理空闲 client(可由定时任务触发)

可选项(new PrismaFactory(options)):

  • createClient(databaseUrl, meta):创建 client(必填)
  • validateFingerprint(client, meta):指纹校验(可选)
  • maxClients:最大 client 数量,默认 2000
  • idleTtlMs:空闲回收阈值(可选)
  • logger:可选日志接口

错误与排查

本包会抛出以下错误(均带 code 字段,便于统一处理):

  • TenantContextMissingError:未绑定上下文或缺少 tenantId
  • TenantNotFoundError:控制库找不到租户
  • TenantStatusError:租户状态不在允许列表
  • TenantRegistryUnavailableError:控制库/registry adapter 不可用(可带 cause
  • TenantDbConnectionLimitError:超过 maxClients
  • TenantFingerprintMismatchError:租户库指纹不一致

本地开发 / 构建

npm install
npm run clean
npm run build
  • 产物输出到 dist/
  • 发布/使用侧通常只需要 dist/(本项目 files 也仅包含 dist

更多说明

更完整的「打包流程 + 使用说明」见:PACKAGING_AND_USAGE.md