@denis_bruns/base-database-service v1.0.0
@denis_bruns/base-database-service
A foundational TypeScript library for building customizable database services with typed queries, filter expressions, and pagination.
Overview
@denis_bruns/base-database-service provides an abstract base class and utility functions to help developers implement clean, consistent database access layers across various data stores (e.g., SQL, NoSQL). It leverages filter expressions, pagination, and type-safe queries for a robust, extensible approach.
This package is part of the larger @denis_bruns ecosystem of libraries that emphasize clean architecture, testability, and modularity.
Key Features
Base Database Service
- An abstract class to derive your own database services.
- Handles pagination, filtering, and query preparation.
Filter Expression Builder
- An abstract class for translating filters (
IFilterQuery) into a store-specific query format. - Extendable for DynamoDB, MongoDB, SQL, or other implementations.
- An abstract class for translating filters (
Pagination and Validation
- Built-in helpers (
calculatePaginationValues,validatePagination) to keep pagination safe and consistent. validateValuefunction to detect potential NoSQL injections or invalid fields.
- Built-in helpers (
Separation of Concerns
IQueryExecutorinterface to separate query building from query execution.- Clean architecture approach that fosters testability and maintainability.
Installation
Install via npm:
npm install @denis_bruns/base-database-serviceOr via yarn:
yarn add @denis_bruns/base-database-serviceBasic Usage
Below is a simplified example showing how to extend BaseDatabaseService and build a custom expression builder.
1. Create Your Expression Builder
import {
BaseExpressionBuilder,
IFilterQuery,
IDatabaseExpression,
} from '@denis_bruns/web-core-ts';
// Custom expression interface
export interface MyExpression extends IDatabaseExpression {
filterExpression: string;
expressionValues: Record<string, any>;
}
// Extend the base expression builder to handle your filters
export class MyExpressionBuilder extends BaseExpressionBuilder<MyExpression> {
buildFilterExpression(filters: IFilterQuery[]): MyExpression {
// Example of building a simple 'field = value' expression
const filterExpression = filters
.map((f, idx) => `${f.field} ${f.operator} :val${idx}`)
.join(' AND ');
const expressionValues = filters.reduce((acc, f, idx) => {
return { ...acc, [`:val${idx}`]: f.value };
}, {});
return { filterExpression, expressionValues };
}
}2. Create Your Query Executor
import { IQueryExecutor } from '@denis_bruns/web-core-ts';
import { MyExpression } from './MyExpressionBuilder';
export class MyQueryExecutor implements IQueryExecutor<MyExpression, any> {
constructor(private readonly dataSource: any[]) {
// 'dataSource' could be an array (for testing) or a real database client
}
async executeQuery(params: MyExpression, _client: any): Promise<any[]> {
// This is just a mock example; you'd integrate with your real DB logic
// For demonstration, let's just return the full dataSource:
return this.dataSource;
}
}3. Extend BaseDatabaseService
import {
BaseDatabaseService,
IGenericFilterQuery,
IPaginationQuery,
} from '@denis_bruns/base-database-service';
import { MyExpressionBuilder, MyExpression } from './MyExpressionBuilder';
import { MyQueryExecutor } from './MyQueryExecutor';
export class MyDatabaseService extends BaseDatabaseService<MyExpression, any> {
constructor(
tableName: string,
private readonly mockData: any[]
) {
super(
tableName,
'id', // primary key name
new MyExpressionBuilder(),
new MyQueryExecutor(mockData)
);
}
protected async prepareQueryParameters(query: IGenericFilterQuery) {
const { filters = [], pagination = {} } = query;
const { limit, offset, page } = this.calculatePaginationValues(pagination as IPaginationQuery);
// Build the expression using our custom builder
const params = this.expressionBuilder.buildFilterExpression(filters);
return {
params,
limit,
offset,
page,
pagination
};
}
protected processResults<T>(
items: any[],
limit: number,
offset: number
): T[] {
// Example: slice items for pagination
return items.slice(offset, offset + limit) as T[];
}
protected handleError(error: any): void {
console.error('Database error:', error);
}
}4. Use Your Derived Service
import { IGenericFilterQuery } from '@denis_bruns/web-core-ts';
async function example() {
// Sample data to simulate a store
const dataSource = [
{ id: '1', name: 'Item 1' },
{ id: '2', name: 'Item 2' },
{ id: '3', name: 'Item 3' },
];
const dbService = new MyDatabaseService('my-table', dataSource);
const query: IGenericFilterQuery = {
filters: [{ field: 'name', operator: '=', value: 'Item 1' }],
pagination: { page: 1, limit: 10 },
};
const results = await dbService.fetchWithFiltersAndPagination(query, null);
console.log('Paginated and filtered results:', results);
}Additional Utilities
validatePagination: Ensures page/limit/offset are integers.validateValue: Checks for forbidden patterns ($where,$regex, etc.) to prevent potential NoSQL injection.calculatePaginationValues: Convertspage,limit, and optionaloffsetto final integers used by your database queries.
Related Packages
@denis_bruns/web-core-ts
Foundational interfaces and types for building modular, testable web applications.@denis_bruns/nosql-mongodb-service
Ready-to-use NoSQL data service built on MongoDB.@denis_bruns/nosql-dynamodb-service
A DynamoDB-specific implementation leveraging expression builders and typed queries.
Contributing
Contributions are always welcome! If you have an idea, bug report, or improvement, please open an issue or submit a Pull Request on GitHub.
License
This project is licensed under the MIT License.