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.
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
urlif you gave one, or assembled fromhost/port/database/username/password, and redacts credentials before ever logging it. - Dynamically imports the real driver,
mongodb,typeorm, orredis, only for the datastore types you've actually configured. None of them are hard dependencies of the framework itself. Configure amongodbdatastore withoutmongodbinstalled, and you get a clear "install it withyarn 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
@DataStorealready stamped onto the class at decoration time, or by an explicitentitieslist in that datastore's own config, and confirms that match by writing the datastore's name onto the class'sdatasourceproperty. That property is the same one@Repositoryand@Transactionalread 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;
}
| Decorator | What 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
RepoUtilswrapper around it. - Transactions:
@Transactional, nested transactions, and compensating rollbacks across connections.