dynamic-orm
Version:
A flexible and powerful dynamic ORM for SQL databases with Redis caching support and many-to-many relationship handling
241 lines (192 loc) • 6.31 kB
Markdown
# Dynamic ORM
A flexible and powerful ORM for SQL databases with Redis caching support.
## Features
- Fluent query builder with support for complex filtering and pagination
- Support for table relationships with automatic JOIN generation
- Redis caching for improved performance
- Transaction support
- TypeScript support with full type definitions
## Installation
```bash
npm install dynamic-orm
```
## Basic Usage
```javascript
const { createORM } = require('dynamic-orm');
const { prepare, transaction } = require('your-database-module');
const redis = require('your-redis-module');
// Create ORM instance
const orm = createORM({
db: { prepare, transaction }, // Your database adapter
redis: redis, // Your Redis client
useCache: true // Enable caching
});
// Create a model for a specific table
const userModel = orm.createModel('users', {
primaryKey: 'id',
searchableFields: ['firstName', 'lastName', 'email'],
defaultLimit: 50,
maxLimit: 500
});
// Now you can use the model to interact with the database
async function example() {
// Find all users with filtering, pagination, and sorting
const result = await userModel.findAll({
filters: {
role: 'admin',
status: 'active',
createdAt: { gt: '2023-01-01' }
},
sort: { lastName: 'asc', firstName: 'asc' },
pagination: { page: 2, limit: 25 },
fields: ['id', 'firstName', 'lastName', 'email'],
search: 'john'
});
console.log(result.data); // Array of user objects
console.log(result.pagination); // Pagination metadata
// Find a user by ID
const user = await userModel.findById('user-123');
// Find a user by email
const userByEmail = await userModel.findByField('email', 'user@example.com');
// Create a new user
const newUser = await userModel.create({
firstName: 'John',
lastName: 'Doe',
email: 'john@example.com'
});
// Update a user
const updatedUser = await userModel.update('user-123', {
status: 'inactive'
});
// Delete a user
await userModel.delete('user-123');
// Count users
const count = await userModel.count({ status: 'active' });
}
```
## Advanced Usage
### Relationships
You can fetch related data using the `relations` option:
```javascript
const result = await userModel.findAll({
filters: { status: 'active' },
relations: [
{
table: 'orders',
foreignKey: 'userId',
localKey: 'id',
as: 'orders',
type: 'many', // or 'left', 'inner', 'right' for JOIN types
select: ['id', 'total', 'createdAt'],
filters: { status: 'completed' }
}
]
});
// Result will include user records with their orders as nested objects
console.log(result.data);
```
### Many-to-Many Relationships
For many-to-many relationships that use a junction table, use the `through` option:
```javascript
// Example: Vehicles with their features through a junction table
const result = await vehicleModel.findAll({
filters: { status: 'active' },
relations: [
{
table: 'features', // Target table (features)
foreignKey: 'id', // Primary key in the features table
as: 'features', // Property name in the result
select: ['id', 'name'], // Fields to select from features table
through: 'vehicle_feature_links', // Junction table name
throughLocalKey: 'stock_id', // FK in junction table that references the main table (vehicles)
throughForeignKey: 'feature_id'// FK in junction table that references the related table (features)
}
]
});
// Each vehicle will have an array of features
console.log(result.data[0].features); // [{id: 1, name: 'GPS'}, {id: 2, name: 'Bluetooth'}, ...]
```
This approach handles the two joins required for many-to-many relationships and properly organizes the results, avoiding duplicates.
#### Simplified Arrays for Single Fields
When you select just a single field from a many-to-many relationship, the ORM automatically simplifies the result to an array of values rather than an array of objects:
```javascript
// Example: Getting just feature names as a simple array
const result = await vehicleModel.findAll({
filters: { status: 'active' },
relations: [
{
table: 'features',
foreignKey: 'id',
as: 'features',
select: ['name'], // When selecting just one field, it returns a simplified array
through: 'vehicle_feature_links',
throughLocalKey: 'stock_id',
throughForeignKey: 'feature_id'
}
]
});
// Each vehicle will have features as a simple array of strings
console.log(result.data[0]);
// Output:
// {
// stock_id: 'ABC123',
// make: 'Toyota',
// model: 'Camry',
// features: ['GPS Navigation', 'Bluetooth', 'Backup Camera']
// }
```
This makes it easier to work with lists of related values when you only need a single property.
### Transactions
Execute multiple operations in a transaction:
```javascript
const result = await orderModel.withTransaction(async (tr) => {
// Create the order
const order = await tr.create({
customerId: 'cust-123',
total: 99.99
});
// Add order items
await tr.executeQuery(
'INSERT INTO order_items (order_id, product_id, quantity) VALUES (?, ?, ?)',
[order.id, 'prod-456', 2]
);
return order;
});
```
### Custom Queries
Execute raw SQL queries when needed:
```javascript
const results = await userModel.executeQuery(
'SELECT id, email FROM users WHERE last_login > ? AND role = ?',
['2023-01-01', 'admin']
);
```
## TypeScript Support
The package includes full TypeScript definitions:
```typescript
import { createORM, Types } from 'dynamic-orm';
// Define your record type
interface User {
id: string;
firstName: string;
lastName: string;
email: string;
role: string;
status: string;
createdAt: string;
}
// Create strongly typed model
const orm = createORM({ db, redis, useCache: true });
const userModel = orm.createModel<User>('users', {
primaryKey: 'id',
searchableFields: ['firstName', 'lastName', 'email']
});
// Now all queries will be properly typed
const result = await userModel.findAll({
filters: { status: 'active' }
});
// result.data will be User[]
const users: User[] = result.data;
```
## License
MIT