Skip to main content

Middleware

Middleware allows you to intercept and transform queries across your application.

Using Middleware

Apply middleware to create a new ORM instance with modified behavior:

const dbWithMiddleware = db.middleware([
tenancy({ org: currentOrg }),
userstamps({ user: currentUser }),
]);

// All queries through dbWithMiddleware are transformed
const posts = await dbWithMiddleware.findMany("post", {
select: ["id", "title"],
});

Example Middleware

tenancy

export const tenancy = ({ org }: { org: Organisation }): Middleware => ({
where: (config, modelName, where) => {
if ("organisationId" in getModel(config.models, modelName).fields) {
return { ...where, organisaitonId: org.id };
} else {
return where;
}
},
values: (config, modelName, values) => {
if ("organisationId" in getModel(config.models, modelName).fields) {
return { ...values, organisationId: org.id };
} else {
return values;
}
},
});


const dbTenant = db.middleware([
tenancy({ org: currentOrg }),
]);

// All queries automatically filter by orgId
const posts = await dbTenant.findMany("post", {
select: ["id", "title"],
// WHERE orgId = currentOrg.id is automatically added
});

userstamps

export const userstamps = ({ user }: { user: User }): Middleware => ({
values: (config, modelName, values) => {
if ("createdById" in getModel(config.models, modelName).fields) {
return { ...values, updatedById: values["createdById"] ?? user.id };
} else {
return values;
}
},
set: (config, modelName, set) => {
if ("updatedById" in getModel(config.models, modelName).fields) {
return { ...set, updatedById: set["updatedById"] ?? user.id };
} else {
return set;
}
},
});

const dbWithMiddleware = db.middleware([
tenancy({ org }), // add a where clause on organisationId to every table that has an organisationId column
userstamps({ user: currentUser }), // Auto-set created_by/updated_by
]);

Automatically set createdBy and updatedBy fields:

import { userstamps } from "@casekit/orm";

const dbUser = db.middleware([
userstamps({ user: currentUser }),
]);

// Creates automatically set createdBy
await dbUser.createOne("post", {
values: { title: "My Post", content: "..." },
// createdBy = currentUser.id is automatically added
});

// Updates automatically set updatedBy
await dbUser.updateOne("post", {
set: { title: "Updated Title" },
where: { id: 1 },
// updatedBy = currentUser.id is automatically added
});

Middleware Hooks

Middleware can intercept different aspects of queries:

where

Transform WHERE clauses for read operations:

const middleware: Middleware = {
where: (config, modelName, where) => {
// Transform the where clause
return {
...where,
active: true, // Add active filter
};
},
};

values

Transform values for CREATE operations:

const middleware: Middleware = {
values: (config, modelName, values) => {
return {
...values,
createdAt: new Date(), // Add timestamp
};
},
};

set

Transform SET values for UPDATE operations:

const middleware: Middleware = {
set: (config, modelName, set) => {
return {
...set,
updatedAt: new Date(), // Add timestamp
};
},
};

Operation Hooks

Override entire operations:

const middleware: Middleware = {
findOne: async (db, modelName, query) => {
console.log(`Finding ${modelName}`, query);
// Call the original or return custom data
return db.findOne(modelName, query);
},

createOne: async (db, modelName, query) => {
console.log(`Creating ${modelName}`, query);
return db.createOne(modelName, query);
},

// Also: findMany, count, createMany, updateOne, updateMany, deleteOne, deleteMany
};

Combining Middleware

Apply multiple middleware in order:

const db = orm(config).middleware([
tenancy({ org }), // Applied first
userstamps({ user }), // Applied second
auditLog, // Applied third
]);

Transformations are composed, so a query goes through each middleware's where, values, or set function in order.

Model Restriction

Restrict access to specific models:

const userDb = db.restrict(["user", "profile"]);

// ✅ These work
await userDb.findMany("user", { ... });
await userDb.findMany("profile", { ... });

// ❌ This throws at runtime
await userDb.findMany("post", { ... }); // Error!

Useful for creating scoped database instances in multi-tenant applications - for example allowing limited access to a set of global tables shared between tenants.