@adonisjs/lucid
Version:
SQL ORM built on top of Active Record pattern
160 lines (159 loc) • 6.29 kB
JavaScript
/*
* @adonisjs/lucid
*
* (c) AdonisJS
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
import { BaseTransformer } from '@adonisjs/core/transformers';
import { Database } from "../src/database/main.js";
import { Adapter } from "../src/orm/adapter/index.js";
import { BaseModel } from "../src/orm/base_model/index.js";
import { QueryClient } from "../src/query_client/index.js";
import { DatabaseTestUtils } from '../src/test_utils/database.js';
import { defineTransformerBindings } from "../src/bindings/transformer.js";
/**
* AdonisJS service provider that wires up the entire Lucid ORM layer.
*
* Responsibilities:
* - Registers the {@link Database} singleton and the default
* {@link QueryClient} in the IoC container.
* - Attaches the ORM {@link Adapter} to {@link BaseModel}.
* - Registers the `db` macro on {@link TestUtils} for test helpers.
* - Binds REPL helpers when running in the `repl` environment.
* - Registers the `unique` / `exists` VineJS validation rules when
* VineJS is present.
* - Optionally enables pretty-printed debug query logging.
*
* @example
* // providers/database_provider.ts is auto-discovered by AdonisJS.
* // No manual instantiation is required.
*/
export default class DatabaseServiceProvider {
app;
/**
* @param {ApplicationService} app - The AdonisJS application instance
* used to access the IoC container, configuration, and environment
* information.
*/
constructor(app) {
this.app = app;
}
/**
* Registers REPL bindings so that interactive database helpers
* (e.g. `db`, `User`) are available when the application is
* started with `node ace repl`.
*
* This is a no-op in any environment other than `repl`.
*/
async registerReplBindings() {
if (this.app.getEnvironment() === 'repl') {
const { defineReplBindings } = await import('../src/bindings/repl.js');
defineReplBindings(this.app, await this.app.container.make('repl'));
}
}
/**
* Registers the database-backed `unique` and `exists` validation
* rules on VineJS when the application is using VineJS as its
* validation engine.
*
* This is a no-op when VineJS is not installed or configured.
*
* @param {Database} db - The resolved {@link Database} singleton
* that will be passed into the validation rule implementations.
*/
async registerVineJSRules(db) {
if (this.app.usingVineJS) {
const { defineValidationRules } = await import('../src/bindings/vinejs.js');
defineValidationRules(db);
}
defineTransformerBindings(BaseTransformer);
}
/**
* Registers a `db` macro on the core {@link TestUtils} class.
* The macro is lazily set up via `container.resolving('testUtils')`
* so it is only wired when tests actually request the `testUtils`
* binding.
*
* @example
* // Inside a test file
* const { db } = await TestUtils.create(app)
* await db.migrate()
*/
async registerTestUtils() {
this.app.container.resolving('testUtils', async () => {
const { TestUtils } = await import('@adonisjs/core/test_utils');
TestUtils.macro('db', (connectionName) => {
return new DatabaseTestUtils(this.app, connectionName);
});
});
}
/**
* Attaches a listener to the `db:query` event that pretty-prints
* every executed query to the console. Only active when the
* `prettyPrintDebugQueries` flag is enabled in the `database.ts`
* configuration file.
*
* @param {Database} db - The resolved {@link Database} singleton
* whose config is checked and whose `prettyPrint` handler is used.
*/
async prettyPrintDebugQueries(db) {
if (db.config.prettyPrintDebugQueries) {
const emitter = await this.app.container.make('emitter');
emitter.on('db:query', db.prettyPrint);
}
}
/**
* Lifecycle hook invoked by AdonisJS during the **register** phase.
*
* Registers the following singletons in the IoC container:
* - `Database` (also aliased as `lucid.db`) — the central connection
* manager, built from the `database` config key.
* - `QueryClient` — a query client bound to the **default**
* connection, convenient for simple queries without explicitly
* choosing a connection.
*/
register() {
this.app.container.singleton(Database, async (resolver) => {
const config = this.app.config.get('database');
const emitter = await resolver.make('emitter');
const logger = await resolver.make('logger');
const db = new Database(config, logger, emitter);
return db;
});
this.app.container.singleton(QueryClient, async (resolver) => {
const db = await resolver.make('lucid.db');
return db.connection();
});
this.app.container.alias('lucid.db', Database);
}
/**
* Lifecycle hook invoked by AdonisJS during the **boot** phase,
* after all providers have registered their bindings.
*
* - Sets the ORM {@link Adapter} on {@link BaseModel} so that all
* model classes share a single, application-scoped adapter.
* - Calls the remaining setup helpers: debug query logging, test
* utilities, REPL bindings, and VineJS rules.
*/
async boot() {
const db = await this.app.container.make('lucid.db');
BaseModel.$adapter = new Adapter(db);
await this.prettyPrintDebugQueries(db);
await this.registerTestUtils();
await this.registerReplBindings();
await this.registerVineJSRules(db);
}
/**
* Lifecycle hook invoked by AdonisJS during application shutdown.
*
* Gracefully closes **all** open database connections managed by the
* {@link Database} connection manager so that no sockets or pools
* are left dangling.
*/
async shutdown() {
const db = await this.app.container.make('lucid.db');
await db.manager.closeAll();
}
}