Skip to main content

Datastores & Connections

Every model needs somewhere to actually live. This page covers pointing an entity at a configured datastore, and reaching a raw connection directly when you need one outside of an entity's own repository. It's not about querying or modifying data day to day, for that see Repository & RepoUtils, this page is specifically about the connection itself.

@DataStore(name)

Every entity declares which configured datastore it belongs to:

import { ModelDecorators } from '@rapidrest/service-core';
const { DataStore } = ModelDecorators;

@DataStore('mongo')
export class Pet extends BaseMongoEntity {}

name is a key under datastores in your configuration:

conf.defaults({
datastores: {
mongo: {type: 'mongodb', host: 'localhost', database: 'my_api'},
},
});

MongoDB, PostgreSQL, and SQLite are all supported for entity storage, SQL datastores go through TypeORM under the hood, so the same entity/column decorators work regardless of which one you pick. Redis is used separately, for caching, see Caching & Sharding.

Required

Leaving @DataStore(...) off an entity fails fast at startup, with an error naming the class, not a confusing failure the first time something tries to read or write it.

The ConnectionManager

Every datastore in your config gets connected exactly once, at startup, by a single object: ConnectionManager. You never interact with it directly, but what it does explains a few things about the decorators above and below it that would otherwise look like unrelated magic.

For each entry under datastores, at startup it:

  • Builds the actual connection string, from a bare url if you gave one, or assembled from host/port/database/username/password, and redacts credentials before ever logging it.
  • Dynamically imports the real driver, mongodb, typeorm, or redis, only for the datastore types you've actually configured. None of them are hard dependencies of the framework itself. Configure a mongodb datastore without mongodb installed, and you get a clear "install it with yarn add mongodb" error instead of a cryptic module-not-found deep in a stack trace.
  • For MongoDB specifically, checks whether the deployment is a standalone instance or a replica set/sharded cluster. Standalone MongoDB can't run multi-document transactions at all, which is exactly the case @Transactional's fallback exists to handle.
  • Matches every model class to the one datastore it belongs to, either by the name @DataStore already stamped onto the class at decoration time, or by an explicit entities list in that datastore's own config, and confirms that match by writing the datastore's name onto the class's datasource property. That property is the same one @Repository and @Transactional read later to know which connection a given model actually uses.

Every connection it opens lives in one map, keyed by datastore name, the exact names you write in @DataStore('mongo'), @DataSource('mongo'), or @Redis('cache'). None of those decorators manage their own connection, they all resolve to whatever's sitting in that same shared map. That's also why the failure looks identical no matter which decorator you used to get there: ask for a name that was never connected, and @Repository, @DataSource, and @Redis all fail with the same "Unable to find database connection with name" error.

At shutdown, ConnectionManager closes every connection it opened, MongoDB clients, TypeORM datasources, Redis clients, each the correct way for its kind, as one of the last steps in the server's shutdown sequence.

Direct connection injection

Inject a raw connection into a service when you need one outside of an entity's own repository:

import { DatabaseDecorators } from '@rapidrest/service-core';
import type { RedisClientType } from 'redis';
const { Redis } = DatabaseDecorators;

export class SessionService {
@Redis('cache')
private redis?: RedisClientType;
}
DecoratorWhat it does
@DataSource(name, required?)Injects the raw datasource connection with the given name
@Redis(name?, required?)Shorthand for @DataSource, defaulting name to "redis". Injects a RedisClientType

required defaults to true: if the named connection isn't configured, instantiation fails with an explicit error rather than silently leaving the property undefined. Set it to false for a connection your code can genuinely run without.

What's next

  • Repository & RepoUtils: direct entity access, and the difference between the raw ORM repository and the ACL/cache-aware RepoUtils wrapper around it.
  • Transactions: @Transactional, nested transactions, and compensating rollbacks across connections.