@itrocks/storage v0.0.17
storage
Transforms model objects to and from storage systems.
Overview
@itrocks/storage
is an abstraction layer module offering a unified API for managing data across various storage
systems, such as databases, files, or APIs.
It simplifies CRUD operations and object relationship handling, independent of the underlying data source.
Core components
DataSource: The core abstraction for connecting to and performing operations on a storage system. It provides generic CRUD methods and is designed to be extended for specific storage implementations (e.g., MySQL, PostgreSQL, files, or even APIs).
Entity: An
Entity
represents a model object linked to a persistent resource in the storage system, such as after reading or writing it using data source features. EachEntity
is expected to have a unique Identifier (id
).SearchType: A flexible object query structure used to define search criteria for finding entities in the storage.
Installation
npm i @itrocks/storage
Basic usage
Setting up a data source
To set up a data source, use the createDataSource function, which initializes a DataSource based on a configuration object and registers it under a specific name.
import { createDataSource, ds } from '@itrocks/storage'
// Configure a main MySQL data source
createDataSource({
engine: '@itrocks/mysql',
host: 'localhost',
user: 'root',
password: 'password',
database: 'example_db'
})
// Access the default (main) data source
const dataSource = ds()
CRUD Operations
The following example demonstrates common data access patterns:
// Saving an Entity
await dataSource.save(new User({ name: 'John Doe' }))
// Reading an Entity by ID
const user = await dataSource.read(User, 1)
// Searching for Entities
const activeUsers = await dataSource.search(User, { active: true })
// Deleting an Entity
await dataSource.delete(user)
Creating a Custom Data Source
To create a custom data source implementation, extend the DataSource abstract class and implement its methods:
import {KeyOf, Type} from '@itrocks/class-type'
import {DataSource, Entity} from '@itrocks/storage'
export class MyCustomDataSource extends DataSource
{
constructor(configuration: { someConfigOption: string }) {
super()
// Save custom configuration
}
async delete<T extends object>(object: Entity<T>, property: KeyOf<Entity<T>> = 'id'): Promise<T> {
// Custom delete logic
}
async deleteId<T extends object>(type: Type<T>, id: Identifier, property?: KeyOf<Entity<T>>) {
// Custom delete by id logic
}
async deleteRelatedId<T extends Entity>(object: T, property: KeyOf<T>, id: Identifier) {
// Custom delete by related object property id
}
insertRelatedId<T extends Entity>(object: T, property: KeyOf<T>, id: Identifier) {
// Custom insert by related object property id
}
async read<T extends object>(type: Type<T>, id: Identifier): Promise<Entity<T>> {
// Custom read logic
}
async readCollection<T extends object, PT extends object>(object: Entity<T>, property: KeyOf<T>, type?: Type<PT>): Promise<Entity<PT>[]> {
// Custom collection object read logic
}
async readCollectionIds<T extends object, PT extends object>(object: Entity<T>, property: KeyOf<T>, type?: Type<PT>): Promise<Identifier[]> {
// Custom collection object id read logic
}
async readMultiple<T extends object>(type: Type<T>, ids: Identifier[]): Promise<Entity<T>[]> {
// Custom read multiple objects
}
async save<T extends object>(object: MayEntity<T>): Promise<Entity<T>> {
// Custom save logic
}
async search<T extends object>(type: Type<T>, search: SearchType<T> = {}): Promise<Entity<T>[]> {
// Custom search logic
}
}
Registering the Custom Data Source
Once your custom DataSource is implemented, register it using the createDataSource function:
createDataSource({
engine: __dirname + '/my-custom-data-source',
someConfigOption: 'value'
})
Storage manager Functions API
createDataSource
createDataSource(config: object, dataSource: string = 'main'): DataSource
Creates and registers a new DataSource.
Parameters:
config
: Configuration object for the data source.dataSource
: (Optional) Name of the data source. Defaults to'main'
.
Returns: The registered DataSource.
dataSource
dataSource(dataSource: string = 'main'): DataSource
Retrieves a registered DataSource by name.
ds
ds(dataSource: string = 'main'): DataSource
Alias for dataSource. Retrieves a registered DataSource by name.
Entity Types API
Entity
type Entity<T extends object = object> = T & { id: Identifier }
A model object linked to stored data though an Identifier.
Identifier
type Identifier = BigInt | Number | String
The identifier of the stored version of the model object. It is unique for each model object type.
MayEntity
type MayEntity<T extends object = object> = T | Entity<T>
A model object that may or may not be linked to stored data.
DataSource API
delete
delete(object: Entity<T>): Promise<T>
Deletes an entity from the storage.
Parameters:
object
: The model object, as an Entity linked to a data source storage entry.
Returns: The object is returned without its Identifier because it no longer matches a stored Entity after deletion.
read
read(type: Type<T>, id: Identifier): Promise<Entity<T>>
Reads an Entity from the storage, by its identifier.
Parameters:
type
: The class Type of the Entity to be read.id
: The Identifier of the Entity in the storage.
Returns: A promise resolving to the Entity read from storage.
readCollection
readCollection(object: Entity<T>, property: KeyOf<T>, type?: Type<PT>): Promise<Entity<PT>[]>
Reads a collection of related entities.
Parameters:
object
: The Entity model object related to the collection to be read.property
: The name of the property linking to the collection.type
: (Optional) The class Type of the collection objects.
Returns:
An array of Entity objects matching the property type
.
save
save(object: MayEntity<T>): Promise<Entity<T>>
Saves an entity.
If the object
is not an Entity,
it will be inserted into the storage and transformed into an Entity.
Parameters:
object
: The model object to save into the storage.
Returns: The saved Entity.
search
search(type: Type<T>, search?: SearchType<T>): Promise<Entity<T>[]>
Searches for entities matching the criteria.
Parameters:
type
: The class Type of the entities to search.search
: The search criteria as a flexible query object.
SearchType
export type SearchType<T extends object = object> = Partial<Record<KeyOf<T>, any>> & Record<string, any>
A SearchType
is an object used to define search criteria for finding entities in the storage system.
It maps property names of the target entity to the corresponding values to search for.
This allows flexible and dynamic queries based on the properties and their expected values.
Planned Enhancements
Property Paths: In future versions, SearchType will support advanced search criteria using property paths. For example, to search for a User whose friends' birth city is "Paris":
{ 'friends.birthCity.name': 'Paris' }
. This will simplify querying related objects.Search Functions: The SearchType will also support functions for more complex criteria beyond simple equality. Examples include:
{ birthDate: duringYear(1976), age: greaterThan(48) }
Combining these features will be possible, such as: { 'friends.birthDate': before(new Date('1976-04-25')) }.