Ent Framework
Ent Framework is a TypeScript-first library designed for interacting with PostgreSQL databases, presenting a graph-like representation of entities rather than a traditional ORM approach. It is currently stable at version 2.26.1 and appears to be actively maintained with regular updates. Key differentiators include its robust solution for the 'N+1 selects' problem through query batching and coalescing, built-in support for microsharding and intelligent replication lag tracking to optimize read operations, and a comprehensive row-level security (privacy layer) system that defines access based on entity relationships. The framework also emphasizes immutability for entity properties and offers a pluggable architecture, allowing integration into existing database setups. It aims to simplify scaling and security concerns for complex business logic by abstracting away much of the underlying SQL complexities.
Common errors
-
TypeError: Reflect.metadata is not a function
cause TypeScript decorators metadata emission is disabled in `tsconfig.json`.fixAdd or ensure `"experimentalDecorators": true` and `"emitDecoratorMetadata": true` are in `compilerOptions` in `tsconfig.json`. -
Error: connect ECONNREFUSED <DB_HOST>:<DB_PORT>
cause The application cannot connect to the PostgreSQL database. This could be due to incorrect connection URI, database server not running, firewall issues, or incorrect credentials.fixVerify the `connectionUri` in `EntContext` initialization. Ensure the PostgreSQL server is running, accessible from the application's host, and that firewall rules permit the connection. Check user credentials and database existence. -
Error: relation "<table_name>" does not exist
cause The database schema does not match the defined Ent classes. This usually happens when an Ent class is defined but the corresponding table or column does not exist in the database, or schema migrations have not been applied.fixRun database migrations to create or update the tables and columns corresponding to your Ent definitions. Ensure your Ent class names correctly map to table names (considering potential naming conventions).
Warnings
- breaking Major versions of Ent Framework may introduce breaking changes to the core API or configuration schemas, particularly around how Ent classes are defined, how relationships are managed, or changes to the database abstraction layer. Always consult the release notes when upgrading.
- gotcha Ent Framework leverages TypeScript decorators, which require `emitDecoratorMetadata` and `experimentalDecorators` to be enabled in your `tsconfig.json`. Failing to do so will result in runtime errors related to decorator usage.
- gotcha While Ent Framework handles query batching and N+1 problems automatically, improper use of raw SQL or bypassing the framework's loading mechanisms can reintroduce performance bottlenecks and negate the benefits of its optimized query patterns.
- gotcha Schema synchronization and migration are critical for any database framework. Ent Framework often requires careful management of your database schema to match your Ent definitions. Automatic migrations might not cover all edge cases or production scenarios.
- gotcha Correctly configuring microsharding and replication lag tracking can be complex. Misconfigurations can lead to data consistency issues (e.g., reading stale data from replicas) or routing requests to incorrect shards, causing data not found errors or performance degradation.
Install
-
npm install ent-framework -
yarn add ent-framework -
pnpm add ent-framework
Imports
- Ent
const Ent = require('ent-framework').Ent;import { Ent } from 'ent-framework'; - EntContext
import { Context } from 'ent-framework';import { EntContext } from 'ent-framework'; - Field
import { Prop } from 'ent-framework';import { Field } from 'ent-framework';
Quickstart
import { Ent, EntContext, Field, UUID, PrimaryKey, UUIDField, StringField, BooleanField, EntCreationOptions, EntQueryContext } from 'ent-framework';
interface UserData {
id: UUID;
name: string;
isActive: boolean;
}
class User extends Ent<UserData> {
@Field(UUIDField())
id: UUID = PrimaryKey.empty();
@Field(StringField())
name: string = '';
@Field(BooleanField({ defaultValue: true }))
isActive: boolean = true;
static create(context: EntQueryContext, data: Partial<UserData>, options?: EntCreationOptions): User {
return super.create(context, data, options) as User;
}
static load(context: EntQueryContext, id: UUID): Promise<User | null> {
return super.load(context, id) as Promise<User | null>;
}
}
async function runExample() {
// In a real application, DATABASE_URL would point to your PostgreSQL instance.
// For this example, we simulate a context setup.
const entContext = new EntContext({
// Replace with actual database connection configuration
// For demonstration, we'll use a placeholder URL.
connectionUri: process.env.DATABASE_URL ?? 'postgresql://user:password@host:port/database',
// Other configuration like sharding rules, logging, etc.
logLevel: 'debug'
});
try {
await entContext.init();
console.log('EntContext initialized.');
// Assume schema migration/sync is handled externally or by framework setup
const newUser = await User.create(entContext, { name: 'Alice', isActive: true });
console.log(`Created user: ${newUser.name} (ID: ${newUser.id})`);
const loadedUser = await User.load(entContext, newUser.id);
if (loadedUser) {
console.log(`Loaded user: ${loadedUser.name}, Active: ${loadedUser.isActive}`);
} else {
console.log('User not found.');
}
// Example of another operation (e.g., updating)
if (loadedUser) {
const updatedUser = await loadedUser.update(entContext, { isActive: false });
console.log(`Updated user: ${updatedUser.name}, Active: ${updatedUser.isActive}`);
}
} catch (error) {
console.error('Error during example run:', error);
} finally {
await entContext.close();
console.log('EntContext closed.');
}
}
runExample();