Express Integration

Middleware and context resolvers specifically built for Express.js applications. This package intercepts incoming HTTP requests, resolves the tenant, and injects the context for downstream routing.

@node-tenant/tenant-express

Overview

What is a middleware? In Express.js, a middleware is a function that catches an incoming HTTP request from a user before it reaches your route code.

Why do we need this? When a user visits your app, your server needs to figure out which customer they belong to so it doesn't accidentally show them someone else's data. @node-tenant/tenant-express provides a ready-made middleware that looks at the incoming request, figures out the customer, and securely locks them into their own sandbox (using AsyncLocalStorage) for the duration of that request.

Real-world Analogy

Imagine your Express app is an exclusive nightclub. The Middleware is the Bouncer at the front door. He checks everyone's ID card (the Tenant ID), verifies they are on the guest list (checks the database using TenantManager), and hands them a specific colored wristband (the Tenant Context). Once inside, the bartenders (your route handlers) know exactly what drinks to serve them based on the wristband color.

Warning: This package relies heavily on @node-tenant/tenant-core. You must initialize a TenantManager and give it to the middleware so the Bouncer has a guest list to check against!

Installation

Install this package in your Express.js project. You will also need @node-tenant/tenant-core if you haven't installed it already.

npm install @node-tenant/tenant-express

Detailed Setup Example

How to use it: You add the tenantMiddleware to your Express app using app.use(). You should do this before you define any routes, so that every single route is protected.

import express from 'express';
import { TenantManager, TenantContextManager } from '@node-tenant/tenant-core';
// Import the middleware and a "resolver" (explained below)
import { tenantMiddleware, headerResolver } from '@node-tenant/tenant-express';

// 1. Initialize the Core Manager (The guest list)
const manager = new TenantManager({ storage });

const app = express();
app.use(express.json());

// 2. Inject the Bouncer (The Tenant Middleware)
app.use(tenantMiddleware({
  manager,
  
  // We tell the middleware to look at the 'x-tenant-id' HTTP header sent by the frontend
  resolver: headerResolver('x-tenant-id'),
  
  // Optional: If someone visits without an ID, kick them out with a 400 Bad Request error.
  requireTenant: true 
}));

// 3. Build your secure routes
app.get('/api/profile', async (req, res) => {
  // We are completely safe here! The middleware guarantees that if this code is running,
  // the user has a valid tenant ID.
  
  // We can ask the global context for the active tenant's details:
  const activeTenant = TenantContextManager.currentOrThrow();
  
  res.json({
    message: `Welcome to the ${activeTenant.name} workspace!`,
    tenantId: activeTenant.id
  });
});

app.listen(3000, () => {
  console.log('Server running on port 3000');
});

Common Mistake to Avoid

Do not put tenantMiddleware after your routes! Express runs code top-to-bottom. If you define app.get('/api/profile') before you call app.use(tenantMiddleware), the profile route will be completely unprotected and anyone could steal data.


Context Resolvers

What is a Resolver? The middleware needs to know where to find the customer's ID in the incoming HTTP request. Does it look at the website URL? Does it look at a secret header sent by a mobile app? A "Resolver" is a tiny helper function that tells the middleware exactly where to look.

1. Header Resolver

How it works: It extracts the tenant ID from an HTTP header.
When to use it: Perfect if you are building an API that will be used by mobile apps (iOS/Android) or external developers.

import { headerResolver } from '@node-tenant/tenant-express';

// It will look for a header named 'x-tenant-id'
const resolver = headerResolver('x-tenant-id');

2. Subdomain Resolver

How it works: It extracts the tenant ID from the website's URL prefix (e.g., if a user visits https://acme.myapp.com, it resolves to acme).
When to use it: Ideal for standard B2B SaaS applications where every customer gets their own subdomain.

import { subdomainResolver } from '@node-tenant/tenant-express';

// Extracts the first segment of the hostname as the tenant slug
const resolver = subdomainResolver();

3. Chain Resolvers

How it works: It lets you combine multiple resolvers together! It will try the first one, and if it fails to find an ID, it will try the next one.
When to use it: If your backend serves BOTH a website (using subdomains) AND a mobile app (using headers).

import { chainResolvers, headerResolver, subdomainResolver } from '@node-tenant/tenant-express';

// Checks headers first. If empty, falls back to checking the subdomain.
const resolver = chainResolvers([
  headerResolver('x-tenant-id'),
  subdomainResolver()
]);