@nl-framework/orm
v0.3.5
Published
MongoDB-first ORM module for Nael Framework with TypeORM-style registration and seeding support.
Readme
@nl-framework/orm
Database-agnostic ORM utilities for the NL Framework with a MongoDB driver included. The package offers TypeORM-inspired module registration helpers, metadata-driven repositories, and seeding utilities that plug into the core DI container while leaving room for additional database drivers.
Installation
bun add @nl-framework/ormQuick start
Register the ORM connection at the root of your application:
import { Module } from '@nl-framework/core';
import { OrmModule, createMongoDriver } from '@nl-framework/orm';
@Module({
imports: [
OrmModule.forRoot({
driver: createMongoDriver({
uri: process.env.MONGO_URI!,
dbName: 'app-db',
}),
connectionName: 'primary',
autoRunSeeds: true,
seedEnvironment: process.env.APP_ENV ?? process.env.NODE_ENV ?? 'default',
}),
],
})
export class AppModule {}Async configuration
For configuration modules that resolve database settings at runtime, use OrmModule.forRootAsync:
import { Module } from '@nl-framework/core';
import { ConfigModule, ConfigService } from '@nl-framework/config';
import { OrmModule, createMongoDriver } from '@nl-framework/orm';
@Module({
imports: [
ConfigModule,
OrmModule.forRootAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: (config: ConfigService) => {
const uri = config.get('database.mongo.uri');
const dbName = config.get('database.mongo.dbName');
return {
driver: createMongoDriver({ uri, dbName }),
autoRunSeeds: true,
};
},
}),
],
})
export class AppModule {}The async variant accepts useFactory, useClass, or useExisting patterns—mirroring other framework modules—so you can compose the ORM connection with any DI-managed configuration source.
Register repositories for feature modules:
@Module({
imports: [OrmModule.forFeature([User])],
})
export class UsersModule {}Entities
Annotate MongoDB documents with the @Document decorator to control collection naming and behaviors.
import { Document } from '@nl-framework/orm';
@Document({ collection: 'users', timestamps: true, softDelete: true })
export class User {
id?: string;
_id?: ObjectId;
email!: string;
name!: string;
}Timestamps automatically manage createdAt/updatedAt fields, while softDelete adds deletedAt support for repositories.
Portable identifiers. Repositories expose an
idstring on every document and accept it for lookups and updates. The underlying Mongo_idfield remains for database compatibility but is managed internally by the repository.
Auto-discovery. The ORM automatically registers every decorated document that has been imported before
OrmModule.forRootexecutes. Provide the optionalentitiesarray only when you need to scope a connection to a specific subset.
Repositories
Inject an OrmRepository (or the Mongo-specific implementation) using the generated token helpers:
import { Inject } from '@nl-framework/core';
import { getRepositoryToken, type OrmRepository } from '@nl-framework/orm';
export class UsersService {
constructor(
@Inject(getRepositoryToken(User))
private readonly users: OrmRepository<User>,
) {}
async listActive() {
return this.users.find();
}
}Repositories provide familiar helpers (find, findOne, insertOne, save, softDelete, restore, etc.) and transparently handle timestamps and soft deletes.
Seeding
Decorate each seeder with @Seed to register metadata used by the automatic runner and history tracker:
import { Seed, type SeederContext } from '@nl-framework/orm';
@Seed({ name: 'initial-users', environments: ['development', 'test'] })
export class InitialUsersSeed {
async run(context: SeederContext) {
const users = await context.getRepository(User);
await users.insertMany([
{ email: '[email protected]', name: 'Admin' },
]);
}
}namebecomes the stable seed identifier (defaults to the class name).environmentslimits execution to matching environments (case-insensitive); omit it to run everywhere.connectionstargets specific ORM connections when you run multiple databases.
When autoRunSeeds is true, the SeedRunner executes during module init, only running seeds that:
- Match the current connection.
- Match the resolved environment (
seedEnvironmentoption, defaulting toprocess.env.NODE_ENV ?? 'default'). - Haven't already been recorded in the driver-provided seed history store.
The Mongo driver persists history in the same database (collection orm_seed_history by default), guaranteeing idempotent startups across deployments. You can still resolve the runner manually via getSeedRunnerToken() if you need to trigger seeds on demand.
Auto-discovery. Seed classes decorated with
@Seedare picked up automatically when their modules are imported. You can still pass an explicitseedsarray toOrmModule.forRootif you want to restrict execution to a subset.
