First-Class Dynamic Fields
Computed, derived, and JSON fields are type-safe, filterable, sortable, and selectable — treated equally to regular columns.
Relayer makes dynamic fields (computed, derived, JSON) a first-class part of your data model — filterable, sortable, selectable, and aggregatable on equal terms with regular columns. Not through raw SQL escape hatches, but as a core design principle. The query DSL is a plain JSON-serializable object, ready to wire up as REST or GraphQL filters.
Currently ships with a Drizzle ORM adapter. Adapters for Kysely, TypeORM, and other ORMs are planned.
First-Class Dynamic Fields
Computed, derived, and JSON fields are type-safe, filterable, sortable, and selectable — treated equally to regular columns.
Complex Filtering
AND, OR, NOT, relation filters (some, every, none), custom SQL in where, 20+ operators. All as a JSON-serializable DSL.
Relations
Batch loading without N+1, per-relation row limits, connect/disconnect/set for managing relations in mutations.
Aggregations
_count, _sum, _avg, _min, _max with groupBy and having. Full support for computed, derived, and JSON fields.
Type-Safe Autocomplete
Full inference for own fields, nested relations, and even nested derived fields across entities.
Multi-Dialect
PostgreSQL, MySQL, SQLite with dialect-aware optimizations for ILIKE, array operators, JSON paths, RETURNING.
Next.js
Type-safe App Router route handlers with validation, lifecycle hooks, SSR direct calls, and configurable field whitelists. Learn more →
NestJS REST
Full-featured REST CRUD with DI-native services, lifecycle hooks, DTO mapping, complex filters, pagination, and Swagger auto-docs. Learn more →
NestJS GraphQL
Code-first GraphQL CRUD with auto-generated schemas, dual pagination, rich filtering, and lifecycle hooks. One decorator, full API. Learn more →
| Package | Description |
|---|---|
@relayerjs/drizzle | Drizzle ORM adapter — main package |
@relayerjs/core | ORM-agnostic types and contracts |
@relayerjs/next | Next.js App Router CRUD integration |
@relayerjs/nestjs-crud | NestJS CRUD controllers with DI, hooks, Swagger |
@relayerjs/nestjs-graphql | NestJS GraphQL code-first CRUD with filtering and pagination |
import { createRelayerDrizzle, createRelayerEntity } from '@relayerjs/drizzle';
import * as schema from './schema'; // your Drizzle schema
// Entity model with computed and derived fieldsconst UserEntity = createRelayerEntity(schema, 'users');
class User extends UserEntity { @UserEntity.computed({ resolve: ({ table, sql }) => sql`upper(${table.name})`, }) displayName!: string;
@UserEntity.derived({ query: ({ db, schema: s, sql, field }) => db .select({ [field()]: sql<number>`count(*)::int`, authorId: s.posts.authorId }) .from(s.posts) .groupBy(s.posts.authorId), on: ({ parent, derived, eq }) => eq(parent.id, derived.authorId), }) postsCount!: number;}
const r = createRelayerDrizzle({ db, schema, entities: { users: User } });
// Query: everything is fully type-safeconst result = await r.users.findMany({ select: { id: true, displayName: true, postsCount: true, posts: { $limit: 5, id: true, title: true }, }, where: { metadata: { role: 'admin' }, posts: { some: { title: { contains: 'Relayer' } } }, }, orderBy: { field: 'postsCount', order: 'desc' }, limit: 10,});
// Aggregation with full computed/derived/JSON supportconst stats = await r.posts.aggregate({ groupBy: ['author.name'], _count: true, _max: { title: true },});