@goatlab/fluent
v0.9.27
Published
Readable query Interface & API generator for TS and Node
Readme
@goatlab/fluent
A TypeScript query builder and ORM wrapper that provides a fluent interface for multiple database types. Built on TypeORM with Zod validation, it offers a unified query syntax across SQL and NoSQL databases with proper type preservation and nested object support.
Installation
npm install @goatlab/fluent
# or
yarn add @goatlab/fluent
# or
pnpm add @goatlab/fluentBasic Usage
import { TypeOrmConnector, f } from '@goatlab/fluent'
import { DataSource } from 'typeorm'
import { z } from 'zod'
// Define your entity
@f.entity('users')
class User {
@f.id()
id: string
@f.property({ required: true, type: 'varchar' })
name: string
@f.property({ type: 'int' })
age?: number
@f.created()
created?: Date
}
// Define your schema
const UserSchema = z.object({
id: z.string().optional(),
name: z.string(),
age: z.number().optional(),
created: z.date().optional()
})
// Create a repository with DataSource or getter function
class UserRepository extends TypeOrmConnector<User> {
constructor(dataSource: DataSource | (() => DataSource)) {
super({
entity: User,
dataSource, // Now supports both DataSource and getter functions
inputSchema: UserSchema
})
}
}
// Use the repository
const userRepo = new UserRepository(dataSource)
// or with a getter function for lazy initialization
const userRepo = new UserRepository(() => getDataSource())
// Insert data
const user = await userRepo.insert({ name: 'John', age: 25 })
// Query data
const users = await userRepo.findMany({
where: { age: { $gte: 18 } },
orderBy: { name: 'asc' },
limit: 10
})
// Find by ID
const user = await userRepo.findById('user-id')
// Update
await userRepo.updateById('user-id', { name: 'Jane' })
// Delete
await userRepo.deleteById('user-id')Key Features
- Unified Query Interface - Consistent API across MongoDB, MySQL, PostgreSQL, SQLite
- TypeORM Integration - Built on the proven TypeORM foundation
- Fluent API - Intuitive chainable query builder with full TypeScript support
- Zod Validation - Automatic input/output validation with Zod schemas
- Decorator-based Models - Simple entity definition using decorators
- Type Safety - Complete type inference and preservation, even in nested queries
- Relations - Full support for One-to-Many, Many-to-One, Many-to-Many relationships
- Advanced Queries - Complex conditions, aggregations, and raw SQL support
- Lazy Initialization - DataSource getter functions for flexible initialization
- Extensible - Base classes for building custom connectors
Advanced Features
Nested Object Queries with Dot Notation (MongoDB)
MongoDB now fully supports nested object queries using dot notation, with proper type preservation:
// Define nested entity structure
export class Address {
@f.property({ required: true, type: 'varchar' })
street: string
@f.property({ required: true, type: 'varchar' })
city: string
@f.property({ required: false, type: 'int' })
zipCode?: number
}
@f.entity('users')
export class User {
@f.id()
id: string
@f.property({ required: true, type: 'varchar' })
name: string
@f.embed(Address)
address?: Address
}
// Query nested fields with dot notation
const users = await userRepo.findMany({
where: {
'address.city': 'New York',
'address.zipCode': { $gte: 10000 }
}
})
// Nested queries preserve types correctly
const users = await userRepo.findMany({
where: {
'profile.settings.notifications': true, // boolean preserved
'profile.settings.maxItems': { $gte: 5 } // number preserved
}
})MongoDB-Specific Behaviors
MongoDB has some specific behaviors that are properly handled:
- CreateDateColumn: In MongoDB,
@f.created()fields always use the current timestamp regardless of any provided value during insertion. This is a TypeORM MongoDB driver behavior.
// MongoDB will ignore the provided created date and use current timestamp
await repo.insert({
name: 'Test',
created: new Date('2020-01-01') // This will be ignored in MongoDB
})- Optimized Simple Queries: Simple queries without OR/AND operators now use a more efficient query structure in MongoDB.
Lazy DataSource Initialization
The TypeORM connector now supports DataSource getter functions, useful for scenarios where the DataSource might not be immediately available:
// Traditional approach
const repo = new UserRepository(dataSource)
// With getter function (lazy initialization)
const repo = new UserRepository(() => container.get(DataSource))
// The DataSource is only accessed when neededType Preservation in Queries
The query builder now properly preserves types when flattening nested objects, ensuring that:
- Numbers remain numbers (not converted to strings)
- Booleans remain booleans
- Arrays are properly handled
- Dates are correctly processed
Supported Databases
All databases supported by TypeORM:
- MySQL / MariaDB
- PostgreSQL
- MongoDB (with full dot notation support)
- SQLite
- Microsoft SQL Server
- Oracle
- CockroachDB
- SAP Hana
- And more...
Testing
The package includes comprehensive test suites that run identically across all supported databases:
# Run all tests
pnpm test
# Run specific database tests
pnpm test:sqlite
pnpm test:mysql
pnpm test:mongodb
pnpm test:postgresql
# Run all database tests concurrently
pnpm test:db:concurrentMigration from Previous Versions
Breaking Changes in v0.8.0
Test Framework: Migrated from Jest to Vitest. Update your test configurations accordingly.
MongoDB Nested Queries: The query builder now properly handles dot notation for nested objects. Review your MongoDB queries to ensure they use the correct syntax.
DataSource Parameter: The
dataSourceparameter in TypeOrmConnector now accepts bothDataSourceand() => DataSourcetypes.
Documentation
For comprehensive documentation, please see the docs directory. Key sections include:
License
MIT
