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

nestjs-multitenant

v2.1.1

Published

A comprehensive multi-tenant solution for NestJS applications with PostgreSQL schema-per-tenant architecture

Readme

nestjs-multitenant

Una solución completa de multi-tenancy para aplicaciones NestJS con arquitectura de esquema por tenant (PostgreSQL) y utilidades para inyección de repositorios, middlewares de resolución de tenant y configuración tipada.

📖 Read the Full Documentation - Comprehensive guides, API reference, and examples

🚀 Características

  • Arquitectura Schema-per-Tenant: Cada tenant tiene su propio esquema de base de datos
  • Soporte Multi-ORM: Compatible con TypeORM y Drizzle ORM
  • Migraciones Automáticas: Migraciones internas para módulo admin (solo Drizzle)
  • Resolución Automática de Tenants: Soporte para múltiples estrategias (header, subdomain, JWT, custom)
  • Pool de Conexiones Dinámico: Gestión eficiente de conexiones por tenant
  • Inyección de Repositorios: Decoradores para inyectar repositorios específicos del tenant
  • Administración de Tenants: Módulo completo para CRUD de tenants
  • Registro de Entidades: Sistema flexible para configurar entidades por tenant
  • TypeScript: Completamente tipado con soporte completo de TypeScript
  • Escalable: Diseñado para aplicaciones de gran escala

Instalación

Instala el paquete y sus peer dependencies requeridas:

pnpm add nestjs-multitenant

Dependencias Peer

Para TypeORM (opción tradicional):

npm install @nestjs/common @nestjs/core @nestjs/typeorm @nestjs/config typeorm pg

Para Drizzle ORM (recomendado para mejor control de migraciones):

npm install @nestjs/common @nestjs/core @nestjs/config drizzle-orm pg
npm install -D drizzle-kit

Requisitos: Node.js >= 22, TypeScript >= 5.9, NestJS 11

📌 Importante: Drizzle ORM es recomendado para multi-tenancy avanzado. Ver Migraciones con Drizzle para más detalles.

🛠️ Configuración Básica

1. Configurar el Módulo Principal (forRoot)

Importa el módulo en tu aplicación e inicializa la configuración:

// app.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { TypeOrmModule } from '@nestjs/typeorm';
import { MultiTenantModule } from 'nestjs-multitenant';

@Module({
  imports: [
    // ⚠️ IMPORTANTE: ConfigModule debe importarse ANTES que MultiTenantModule
    ConfigModule.forRoot({
      isGlobal: true, // Hace que ConfigService esté disponible globalmente
    }),

    // Configuración de la base de datos principal
    TypeOrmModule.forRoot({
      type: 'postgres',
      host: 'localhost',
      port: 5432,
      username: 'postgres',
      password: 'password',
      database: 'multitenant_db',
      schema: 'public',
      synchronize: true,
    }),

    // Configuración del módulo multi-tenant
    MultiTenantModule.forRoot({
      database: {
        host: 'localhost',
        port: 5432,
        username: 'postgres',
        password: 'password',
        database: 'multitenant_db',
      },
      autoCreateSchemas: true,
      enableAdminModule: true, // Habilita el módulo de administración
      platform: 'express', // express o fastify
      customControllers: [CustomTenantAdminController], // Permite cargar tu propia implementacion del controller, omitiendo el controller administrativo, si no se especifica tomara la implementacion propia del modulo interno
      customProviders: [
        createTenantStrategyProvider(TenantAdminService), // Type-safe
      ],
    }),
  ],
})
export class AppModule {}

Nota importante sobre forRoot:

  • La propiedad enableAdminModule, precargara la clase TenantAdminService y solo si la propiedad customControllers se encuentra vacia cargara TenantAdminController; esto brinda la ventaja de especificar tu propio controller.
  • Puedes omitir la clase precargada TenantAdminService, haciendo uso de la propiedad customProviders y especificar tu propia implementación.

2. Configuración Asíncrona (forRootAsync) - v2.1.0+

Para configuraciones dinámicas que requieren inyección de dependencias:

:::note Simplified Configuration (v2.1.0+) El método buildAsyncConfig() simplifica drásticamente la configuración asíncrona. Ver ejemplos completos en Configuración Avanzada. :::

Configuración recomendada con controller admin por defecto:

// app.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { TypeOrmModule } from '@nestjs/typeorm';
import {
  MultiTenantModule,
  createDatabaseConfigFromEnv,
} from 'nestjs-multitenant';

@Module({
  imports: [
    // ⚠️ IMPORTANTE: ConfigModule debe importarse ANTES que MultiTenantModule
    ConfigModule.forRoot({ isGlobal: true }),

    // Conexión principal de la aplicación
    TypeOrmModule.forRoot({
      type: 'postgres',
      host: 'localhost',
      port: 5432,
      username: 'postgres',
      password: 'postgres',
      database: 'multitenant_db',
      schema: 'public',
      synchronize: true,
    }),

    // 🆕 Configuración simplificada con buildAsyncConfig (v2.1.0+)
    MultiTenantModule.forRootAsync(
      MultiTenantModule.buildAsyncConfig({
        ormType: 'typeorm',
        enableAdminController: true, // Incluye controller admin por defecto
        useFactory: (config: ConfigService) => ({
          database: createDatabaseConfigFromEnv(config),
          validationStrategy: 'local',
          autoCreateSchemas: true,
          platform: 'fastify',
        }),
        inject: [ConfigService],
      }),
    ),
  ],
})
export class AppModule {}

Configuración con controller personalizado:

// app.module.ts
MultiTenantModule.forRootAsync(
  MultiTenantModule.buildAsyncConfig({
    ormType: 'typeorm',
    enableAdminController: false, // No incluye controller por defecto
    additionalImports: [CustomTenantAdminModule], // Importa el módulo personalizado con el controller
    managementStrategyProvider: createTenantStrategyProvider(
      CustomTenantAdminService,
    ),
    useFactory: (config: ConfigService) => ({
      database: createDatabaseConfigFromEnv(config),
      validationStrategy: 'local',
      autoCreateSchemas: true,
      platform: 'fastify',
    }),
    inject: [ConfigService],
  }),
);

Nota importante sobre buildAsyncConfig (v2.1.0+):

  • 🆕 Simplificado: Maneja automáticamente la configuración específica del ORM
  • 🆕 Type-Safe: createTenantControllerFactory() para validación en tiempo de compilación
  • 🆕 Flexible: Soporta TypeORM y Drizzle con la misma API
  • Legacy forRootAsync: Aún disponible pero marcado como deprecated
  • 📖 Documentación completa: Ver Configuración Avanzada para todos los escenarios

3. Registro de Entidades para forRootAsync

Cuando uses forRootAsync, es CRÍTICO registrar las entidades ANTES de que se inicialice el módulo:

// entities/index.ts - Crear este archivo PRIMERO
import { EntityRegistry } from 'nestjs-multitenant';
import { User } from './user.entity';
import { Product } from './product.entity';

// IMPORTANTE: Registrar INMEDIATAMENTE al importar
// Opción 1: API fluida (recomendada)
EntityRegistry.getInstance()
  .registerEntity('User', User)
  .registerEntity('Product', Product);

// Opción 2: Registrar múltiples entidades
export const entities = {
  User: User,
  Product: Product,
};
EntityRegistry.getInstance().registerEntities(entities);
// app.module.ts
import { Module } from '@nestjs/common';
import './entities'; // IMPORTAR PRIMERO para registrar entidades
import { MultiTenantModule } from 'nestjs-multitenant';

@Module({
  imports: [
    // ... otros módulos
    MultiTenantModule.forRootAsync({
      // ... configuración
    }),
  ],
})
export class AppModule {}

⚠️ Problema Común: Si registras las entidades después de la inicialización del módulo, getEntityRegistryConfig() retornará un objeto entities vacío.

✅ Solución: Siempre importa el archivo de registro de entidades ANTES que MultiTenantModule.forRootAsync.

🔍 Debug: Usa getEntityRegistryDebugInfo() para verificar el estado del registro:

import { getEntityRegistryDebugInfo } from 'nestjs-multitenant';

// En un endpoint o servicio
const debugInfo = getEntityRegistryDebugInfo();
console.log('Registry state:', debugInfo);
// Output: { entityCount: 2, entities: ['User', 'Product'], presets: ['basic', 'full'] }

4. Variables de Entorno

# .env
DB_TYPE=postgres
DB_HOST=localhost
DB_PORT=5432
DB_USERNAME=postgres
DB_PASSWORD=password
DB_DATABASE=multitenant_db
TENANT_HEADER=x-tenant-id
AUTO_CREATE_SCHEMAS=true
ENABLE_ADMIN_MODULE=true
MULTITENANT_RUN_ADMIN_MIGRATIONS=true  # Para Drizzle (default: true)

📌 Nota: MULTITENANT_RUN_ADMIN_MIGRATIONS controla las migraciones automáticas del módulo admin (solo para Drizzle ORM).

🏗️ Uso del Sistema

Definir Entidades

Para TypeORM:

// entities/user.entity.ts
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';

@Entity('users')
export class User {
  @PrimaryGeneratedColumn('uuid')
  id: string;

  @Column()
  name: string;

  @Column({ unique: true })
  email: string;

  @Column()
  createdAt: Date;
}

Para Drizzle ORM:

// entities/user.entity.ts
import { pgTable, uuid, varchar, timestamp } from 'drizzle-orm/pg-core';
import { prefixSchema } from 'nestjs-multitenant';

// Usar el patrón de schemas recomendado
export const userSchema = prefixSchema('tenant_example');

export const users = userSchema.table('users', {
  id: uuid('id').defaultRandom().primaryKey(),
  name: varchar('name', { length: 255 }).notNull(),
  email: varchar('email', { length: 255 }).notNull().unique(),
  createdAt: timestamp('created_at').defaultNow(),
});

📖 Drizzle Setup: Para guía completa de configuración de Drizzle, ver Setup PostgreSQL

Registrar Entidades

// entities/index.ts
import { EntityRegistry } from 'nestjs-multitenant';
import { User } from './user.entity';
import { Product } from './product.entity';

// Opción 1: Registrar entidades individualmente (API fluida)
EntityRegistry.getInstance()
  .registerEntity('User', User)
  .registerEntity('Product', Product)
  .registerPreset('basic', ['User'])
  .registerPreset('ecommerce', ['User', 'Product']);

// Opción 2: Registrar múltiples entidades a la vez
const entities = {
  User: User,
  Product: Product,
};

EntityRegistry.getInstance()
  .registerEntities(entities)
  .registerPresets({
    basic: ['User'],
    ecommerce: ['User', 'Product'],
  });

Crear un Servicio con Repositorios de Tenant

// services/user.service.ts
import { Injectable } from '@nestjs/common';
import { Repository } from 'typeorm';
import { InjectTenantRepository } from 'nestjs-multitenant';
import { User } from '../entities/user.entity';

@Injectable()
export class UserService {
  constructor(
    @InjectTenantRepository(User)
    private readonly userRepository: Repository<User>,
  ) {}

  async findAll(): Promise<User[]> {
    return this.userRepository.find();
  }

  async create(userData: Partial<User>): Promise<User> {
    const user = this.userRepository.create(userData);
    return this.userRepository.save(user);
  }

  async findById(id: string): Promise<User> {
    return this.userRepository.findOne({ where: { id } });
  }
}

Configurar un Módulo de Funcionalidad

// modules/user.module.ts
import { Module } from '@nestjs/common';
import { createTenantRepositoryProviders } from 'nestjs-multitenant';
import { User } from '../entities/user.entity';
import { UserService } from '../services/user.service';
import { UserController } from '../controllers/user.controller';

@Module({
  providers: [...createTenantRepositoryProviders([User]), UserService],
  controllers: [UserController],
  exports: [UserService],
})
export class UserModule {}

🔧 Configuración Avanzada

Estrategias de Resolución de Tenants

  • Header: Extrae el tenant ID de un encabezado HTTP.
  • Subdominio: Utiliza el subdominio como tenant ID.
  • Dominio: Extrae el tenant ID del dominio.
  • Query Parameter: Obtiene el tenant ID de un parámetro de consulta.
MultiTenantModule.forRootAsync({
  tenantResolution: {
    strategy: 'header' | 'subdomain' | 'jwt' | 'custom',
    headerName: 'x-tenant-id', // Encabezado por defecto
    jwtClaimName: 'tenantId', // Claim por defecto en JWT
    customResolver: (request: unknown) => {
      // Lógica personalizada para resolver tenant
      return 'default-tenant';
    },
  },
});

Nota Importante:

  • Si la estrategia de resolución es header, asegúrate de que el encabezado exista en la solicitud y definir headerName si es diferente de x-tenant-id.
  • Si la estrategia de resolución es subdomain, asegúrate de que el subdominio esté configurado correctamente.
  • Si la estrategia de resolución es jwt, asegúrate de que el token JWT esté presente y válido; ademas, definir jwtClaimName si es diferente de tenantId.
  • Si la estrategia de resolución es custom, asegúrate de proporcionar una función personalizada que devuelva el tenant ID basado en la solicitud.

Pool de Conexiones

MultiTenantModule.forRoot({
  // ... otras configuraciones
  connectionPool: {
    maxConnections: 50,
    idleTimeout: 30000,
    enableCleanup: 30000,
    cleanupInterval: 5000,
  },
});

Configuración de Entidades por Tenant

// Configurar entidades disponibles por tenant
EntityRegistry.getInstance()
  .registerPreset('startup', ['User', 'Project'])
  .registerPreset('enterprise', ['User', 'Project', 'Analytics', 'Billing']);

🔍 Uso Avanzado

Acceso Directo al DataSource del Tenant

import { Injectable } from '@nestjs/common';
import { DataSource } from 'typeorm';
import { InjectTenantDataSource } from 'nestjs-multitenant';

@Injectable()
export class AdvancedService {
  constructor(
    @InjectTenantDataSource()
    private readonly dataSource: DataSource,
  ) {}

  async executeRawQuery(query: string) {
    return this.dataSource.query(query);
  }

  async runTransaction(callback: (manager: EntityManager) => Promise<any>) {
    return this.dataSource.transaction(callback);
  }
}

Factory de Repositorios

import { Injectable } from '@nestjs/common';
import { Repository } from 'typeorm';
import { InjectTenantRepositoryFactory } from 'nestjs-multitenant';
import { User } from '../entities/user.entity';

@Injectable()
export class MultiTenantService {
  constructor(
    @InjectTenantRepositoryFactory(User)
    private readonly userRepositoryFactory: (
      tenantId: string,
    ) => Promise<Repository<User>>,
  ) {}

  async getUsersFromSpecificTenant(tenantId: string) {
    const userRepository = await this.userRepositoryFactory(tenantId);
    return userRepository.find();
  }
}

🔄 Migraciones con Drizzle ORM

Para proyectos con Drizzle ORM, el módulo incluye capacidades avanzadas de migración:

Configuración Automática

El módulo gestiona automáticamente las migraciones del schema de administración:

// drizzle.config.ts
import type { Config } from 'drizzle-kit';

export default {
  schema: './src/entities/**/*.ts',
  out: './drizzle',
  driver: 'pg',
  dbCredentials: {
    url: process.env.DATABASE_URL,
  },
} satisfies Config;

Flujo de Trabajo

  1. Generar migración: drizzle-kit generate
  2. Aplicar migración: drizzle-kit migrate
  3. Migraciones admin: Automáticas controladas por MULTITENANT_RUN_ADMIN_MIGRATIONS

📖 Guía Completa: Ver Migraciones con Drizzle para detalles completos.

Problemas Comunes

Errores de Migración con Drizzle

Problema: Las migraciones de tenant no se aplican correctamente.

Solución: Asegúrate de seguir el patrón prefixSchema:

const prefixSchema = (schema: string) => pgSchema(`tenant_${schema}`);
export const tenantSchema = prefixSchema('your-tenant-id');

Problema: Migraciones del módulo admin no se ejecutan.

Solución: Verifica la variable de entorno:

MULTITENANT_RUN_ADMIN_MIGRATIONS=true

Conexión a Base de Datos

Problema: Errores de conexión o timeout.

Solución: Verifica la configuración de la base de datos y asegúrate de que PostgreSQL esté ejecutándose:

MultiTenantModule.forRoot({
  database: {
    host: 'localhost',
    port: 5432,
    username: 'postgres',
    password: 'password',
    database: 'multitenant_db',
    // Opcional: configuración adicional
    ssl: false,
    synchronize: true, // Solo en desarrollo
    logging: true, // Para debug
  },
});

🔧 Resolución de Problemas

Error: "Nest can't resolve dependencies of the TypeOrmModuleOptions"

Problema: Error al inicializar el módulo con el mensaje:

UnknownDependenciesException [Error]: Nest can't resolve dependencies of the TypeOrmModuleOptions (?).
Please make sure that the argument ConfigService at index [0] is available in the TypeOrmCoreModule context.

Solución: Este error ocurre cuando ConfigModule no está disponible en el contexto del módulo. Asegúrate de:

  1. Importar ConfigModule ANTES que MultiTenantModule:
@Module({
  imports: [
    ConfigModule.forRoot({ isGlobal: true }), // ⚠️ DEBE ir ANTES
    MultiTenantModule.forRoot({ /* config */ }),
  ],
})
  1. Usar configuración global:
ConfigModule.forRoot({
  isGlobal: true, // Hace ConfigService disponible globalmente
});
  1. Para configuración asíncrona, inyectar ConfigService correctamente:
MultiTenantModule.forRootAsync({
  inject: [ConfigService],
  useFactory: (configService: ConfigService) => ({
    // configuración
  }),
});

Error: "Cannot find module 'nestjs-multitenant'"

Solución: Instala las dependencias requeridas:

Para TypeORM:

npm install @nestjs/common @nestjs/core @nestjs/typeorm @nestjs/config typeorm pg

Para Drizzle ORM:

npm install @nestjs/common @nestjs/core @nestjs/config drizzle-orm pg
npm install -D drizzle-kit

Problemas de Conexión a Base de Datos

Problema: Errores de conexión o timeout.

Solución: Verifica la configuración de la base de datos y asegúrate de que PostgreSQL esté ejecutándose:

MultiTenantModule.forRoot({
  database: {
    host: 'localhost',
    port: 5432,
    username: 'postgres',
    password: 'password',
    database: 'multitenant_db',
    // Opcional: configuración adicional
    ssl: false,
    synchronize: true, // Solo en desarrollo
    logging: true, // Para debug
  },
});

🤝 Contribución

  1. Fork el proyecto
  2. Crea una rama para tu feature (git checkout -b feature/amazing-feature)
  3. Commit tus cambios (git commit -m 'Add amazing feature')
  4. Push a la rama (git push origin feature/amazing-feature)
  5. Abre un Pull Request

Versionado y CHANGELOG

Este proyecto sigue SemVer. Las releases se realizan con mensajes de commit semánticos y se documentan en CHANGELOG.md.

🆘 Soporte

Licencia

MIT © Reymi Tech