Moleculer Database Context Integrator
moleculer-context-db is a utility library for Moleculer microservices that integrates database session management directly into the service context. It currently offers built-in support for Mikro-ORM, specifically focusing on providing transaction-safe database sessions for actions. While SQL databases have been thoroughly tested, MongoDB support is noted as experimental. The library streamlines the process of injecting a database EntityManager or session into each Moleculer action's context, ensuring that operations within an action can participate in a single, consistent transaction. The current stable version is 2.0.3. Release cadence is not explicitly stated but aligns with Mikro-ORM major versions due to peer dependencies. Its key differentiator is simplifying transaction management within a Moleculer microservice architecture, abstracting away manual session handling.
Common errors
-
Error: Cannot find module '@mikro-orm/core'
cause Mikro-ORM core package is a peer dependency but has not been installed.fixInstall the core Mikro-ORM package: `npm install @mikro-orm/core` -
Error: Cannot find module 'moleculer'
cause Moleculer is a peer dependency but has not been installed.fixInstall the Moleculer framework: `npm install moleculer` -
Error: Driver not found for type 'sqlite'
cause The specific Mikro-ORM database driver (e.g., `@mikro-orm/sqlite`) for the configured database type is missing.fixInstall the appropriate Mikro-ORM driver for your database type, e.g., `npm install @mikro-orm/sqlite` for SQLite, or `@mikro-orm/mongodb` for MongoDB. -
Error: EntityManager not found in context
cause The `DatabaseContextManager.middleware()` was not correctly added to the Moleculer broker, or the action was called without a context where the EntityManager would be injected.fixEnsure `yourMoleculerBroker.middlewares.add(dbContextManager.middleware());` is called *before* starting the broker and calling actions.
Warnings
- gotcha MongoDB support is considered experimental and has not been as thoroughly tested as SQL database integrations. Users relying on MongoDB should proceed with caution and thorough testing.
- gotcha Moleculer and Mikro-ORM (specifically `@mikro-orm/core` and specific drivers like `@mikro-orm/sqlite`) are peer dependencies. They must be installed separately in your project, and version compatibility should be checked.
- gotcha When configuring MikroConnector for MongoDB, `implicitTransactions` must be set to `true` if you require transaction support and are running a replica set. Failing to do so might result in transaction-related errors.
Install
-
npm install moleculer-context-db -
yarn add moleculer-context-db -
pnpm add moleculer-context-db
Imports
- MikroConnector
const MikroConnector = require('moleculer-context-db').MikroConnector;import { MikroConnector } from 'moleculer-context-db'; - DatabaseContextManager
const DatabaseContextManager = require('moleculer-context-db').DatabaseContextManager;import { DatabaseContextManager } from 'moleculer-context-db'; - middleware
yourMoleculerBroker.middlewares.add(middleware());
yourMoleculerBroker.middlewares.add(DatabaseContextManager.middleware());
Quickstart
import { ServiceBroker } from 'moleculer';
import { MikroConnector, DatabaseContextManager } from 'moleculer-context-db';
import { BaseEntity, Entity, PrimaryKey, Property, MikroORM, Collection } from '@mikro-orm/core';
import { SqliteDriver } from '@mikro-orm/sqlite';
// 1. Define your Mikro-ORM entities (example)
@Entity()
class User extends BaseEntity<User, 'id'> {
@PrimaryKey()
id!: number;
@Property({ unique: true })
username!: string;
@Property()
email!: string;
}
// 2. Instantiate and initialize the MikroConnector
const connector = new MikroConnector<SqliteDriver>();
async function setup() {
await connector.init({
type: 'sqlite',
dbName: ':memory:',
entities: [User],
cache: { enabled: false },
// Ensure schema is synchronized for in-memory DB
migrations: { runMigrations: true, path: './migrations' }, // Dummy path, not used for in-memory
allowGlobalContext: true // Important for direct ORM access in tests/scripts
});
const orm = connector.getOrm();
if (orm) {
await orm.getSchemaGenerator().updateSchema();
}
// 3. Create a Moleculer Service Broker
const broker = new ServiceBroker({
logger: true,
logLevel: 'info'
});
// 4. Instantiate DatabaseContextManager and add its middleware
const dbContextManager = new DatabaseContextManager(connector);
broker.middlewares.add(dbContextManager.middleware());
// 5. Define a Moleculer service that uses the context-injected EntityManager
broker.createService({
name: 'users',
actions: {
async create(ctx) {
// Access the EntityManager from the context
const em = ctx.em;
if (!em) {
throw new Error('EntityManager not found in context');
}
const { username, email } = ctx.params;
const user = em.create(User, { username, email });
await em.persistAndFlush(user);
return user;
},
async list(ctx) {
const em = ctx.em;
if (!em) {
throw new Error('EntityManager not found in context');
}
return em.find(User, {});
}
}
});
await broker.start();
// 6. Call a service action to test
try {
const user1 = await broker.call('users.create', { username: 'john.doe', email: 'john@example.com' });
console.log('Created user:', user1);
const user2 = await broker.call('users.create', { username: 'jane.doe', email: 'jane@example.com' });
console.log('Created user:', user2);
const users = await broker.call('users.list');
console.log('All users:', users);
} catch (error) {
console.error('Error during service call:', error);
} finally {
await broker.stop();
const orm = connector.getOrm();
if (orm) {
await orm.close();
}
}
}
setup();