@reldens/storage Driver Methods
Complete reference for the driver abstraction layer in @reldens/storage: what problem it solves and the full set of methods every driver must implement.
The Abstraction Layer
The @reldens/storage package provides a UNIFIED API across six drivers:
- Knex - SQL query builder, the default driver and the only one bundled with the package.
- Kysely - type safe SQL query builder (optional).
- Drizzle - TypeScript ORM (optional).
- ObjectionJS - built on Knex.js (optional).
- MikroORM - for MongoDB / NoSQL support (optional).
- Prisma - schema-first ORM (optional).
The optional drivers expect their packages installed in the project, passed to the data server through its [driver]Modules option. See @reldens/storage Prisma Setup for the Prisma driver.
Write once, run anywhere:
// Application code - SAME for all drivers
let category = await categoriesRepo.create({name: 'Electronics', slug: 'electronics'});
let products = await productsRepo.loadWithRelations({category_id: category.id}, ['related_reviews']);
let count = await categoriesRepo.count({is_active: 1});
No matter which driver is configured (knex, kysely, drizzle, objection-js, mikro-orm or prisma), the code above works identically.
Without Abstraction
Each ORM has different method names, parameter structures, query syntax, relation loading syntax, and filter operators:
// ObjectionJS
let category = await Category.query().insert({name: 'Electronics'});
let products = await Product.query().where('category_id', category.id).withGraphFetched('related_reviews');
// MikroORM
let category = await em.create(Category, {name: 'Electronics'});
await em.flush();
let products = await em.find(Product, {category_id: category.id}, {populate: ['related_reviews']});
// Prisma
let category = await prisma.category.create({data: {name: 'Electronics'}});
let products = await prisma.product.findMany({where: {category_id: category.id}, include: {related_reviews: true}});
The Contract
Every driver MUST implement:
- Same method signatures - same method names, same parameters.
- Same behaviour - same inputs produce same outputs.
- Same return format - results structure is identical.
- Driver-agnostic code - application code doesn't know which driver is used.
The BaseDriver class defines this contract (lib/base-driver.js).
Public Methods - The Complete API
CREATE operations
- create(params) - create single record
- createWithRelations(params, relations) - create record with nested relations
READ operations (no relations)
- loadAll() - load all records (no filters, respects limit / offset / sort)
- load(filters) - load records by filters (respects limit / offset / sort)
- loadBy(field, fieldValue, operator) - load records by single field
- loadById(id) - load single record by primary key
- loadByIds(ids) - load multiple records by an IDs array
- loadOne(filters) - load first record matching filters
- loadOneBy(field, fieldValue, operator) - load first record by single field
READ operations (with relations)
- loadAllWithRelations(relations) - load all records with relations
- loadWithRelations(filters, relations) - load with filters and relations
- loadByWithRelations(field, fieldValue, relations, operator) - load by field with relations
- loadByIdWithRelations(id, relations) - load by ID with relations
- loadOneWithRelations(filters, relations) - load first record with relations
- loadOneByWithRelations(field, fieldValue, relations, operator) - load first by field with relations
UPDATE operations
- update(filters, updatePatch) - update records by filters
- updateBy(field, fieldValue, updatePatch, operator) - update records by single field
- updateById(id, params) - update record by ID
- upsert(params, filters) - update if the record exists (by ID or by filters), create otherwise
DELETE operations
- delete(filters) - delete records by filters
- deleteById(id) - delete single record by ID
COUNT operations
- count(filters) - count records matching filters
- countWithRelations(filters, relations) - count records with relation filters
UTILITY operations
- rawQuery(content) - execute a raw SQL query (implemented by the data server)
- executeCustomQuery(methodName, methodOptions) - execute a custom model method
- isJsonField(fieldName) - check if a field is JSON type
- parseRelationsString(relationsString) - parse a comma separated relations string to an array
PROPERTY accessors
- databaseName(), id(), name(), tableName(), property(propertyName)
CONFIGURATION properties
- limit - result limit (0 = no limit)
- offset - result offset
- sortBy - sort field name
- sortDirection - sort direction (ASC or DESC)
- select - fields to select (array)
Driver Implementations
- KnexDriver (lib/knex/knex-driver.js) - uses the Knex query builder. Default driver.
- KyselyDriver (lib/kysely/kysely-driver.js) - uses the Kysely query builder.
- DrizzleDriver (lib/drizzle/drizzle-driver.js) - uses Drizzle ORM.
- ObjectionJsDriver (lib/objection-js/objection-js-driver.js) - uses the Objection query builder on top of Knex.
- MikroOrmDriver (lib/mikro-orm/mikro-orm-driver.js) - uses MikroORM EntityManager.
- PrismaDriver (lib/prisma/prisma-driver.js) - uses typed Prisma Client.
The Knex, Kysely and Drizzle drivers extend QueryBuilderDriver (lib/query-builder-driver.js), which implements most of the API once on top of five primitives (insertRow(), selectRows(), update(), delete(), count()). They load relations with one extra query per relation level through RelationsLoader, and turn relation filters into IN (SELECT ...) sub queries, so counts are never inflated by joins.
Filter Syntax
Every driver accepts the same filter objects:
// WHERE field = value
{field: value}
// WHERE field > 10 (operators: GT, GTE, LT, LTE, NE, EQ)
{field: {operator: 'GT', value: 10}}
// WHERE field IN (1, 2, 3)
{field: {operator: 'IN', value: [1, 2, 3]}}
// WHERE field LIKE '%pattern%'
{field: {operator: 'LIKE', value: 'pattern'}}
// WHERE field1 = value1 OR field2 = value2
{OR: [{field1: value1}, {field2: value2}]}
// WHERE field1 = value1 AND field2 = value2
{AND: [{field1: value1}, {field2: value2}]}
Operators are case-insensitive (converted to uppercase). NOT is also supported, AND / OR conditions can be nested, and LIKE wraps the value as %value% (the Knex and ObjectionJS drivers cast JSON columns to text for it). The base BaseDriver.operatorsMap maps GT, GTE, LT, LTE, NE and EQ to their SQL operators, and each driver translates the filters to its own syntax internally.
Related Documentation
- Storage Architecture - Storage layer overview from the Reldens platform perspective.
- @reldens/storage Test Architecture
- @reldens/storage Prisma Setup - Install and use the optional Prisma driver.
- Commands Reference - reldens-storage CLI commands.
- Entities Reference - Entity types exposed via these drivers.
reldens