NestJS: Getting Started
What you get
Drop @relayerjs/nestjs-crud into a NestJS project and get a production-ready REST API:
- Full CRUD — list, get by id, create, update, delete, count
- Filtering —
?where={"published":true}with 20+ operators - Search —
?search=helloacross multiple fields - Pagination — offset or cursor-based, with
nextPageUrl - Relations — load nested data:
?select={"comments":{"id":true}} - Validation — Zod or class-validator, unified error format
- Auth — guards and decorators per route
All type-safe, all configurable, all overridable.
Installation
npm install @relayerjs/nestjs-crud @relayerjs/drizzle drizzle-ormAssumes you already have a NestJS project with @nestjs/common, @nestjs/core, etc.
Quick start (5 minutes)
1. Define your entity
import { createRelayerEntity } from '@relayerjs/drizzle';
import * as schema from '../schema';
export class PostEntity extends createRelayerEntity(schema, 'posts') {}Need computed fields? Extend the class:
const UserBase = createRelayerEntity(schema, 'users');
export class UserEntity extends UserBase { @UserBase.computed({ resolve: ({ table, sql }) => sql`${table.firstName} || ' ' || ${table.lastName}`, }) fullName!: string;}2. Create an entity map
The entity map ties your entity classes together and enables cross-entity type inference:
import { PostEntity } from './post.entity';import { UserEntity } from './user.entity';
export const entities = { users: UserEntity, posts: PostEntity };export type EM = typeof entities;3. Register the module
Call RelayerModule.forRoot() once in your root AppModule. This creates the Relayer client, registers entity providers, and makes everything available globally:
import { Module } from '@nestjs/common';import { RelayerModule } from '@relayerjs/nestjs-crud';
import { db } from './db';import { PostEntity, UserEntity } from './entities';import * as schema from './schema';
@Module({ imports: [ RelayerModule.forRoot({ db, // your Drizzle db instance schema, // Drizzle schema export (tables + relations) entities: [UserEntity, PostEntity], defaultRelationLimit: 50, baseUrl: () => `http://localhost:${process.env.PORT ?? 3000}`, }), PostsModule, ],})export class AppModule {}For dynamic configuration (e.g. reading DB URL from environment):
RelayerModule.forRootAsync({ imports: [ConfigModule], inject: [ConfigService], useFactory: (config: ConfigService) => ({ db: createDb(config.get('DATABASE_URL')), schema, entities: [UserEntity, PostEntity], }),});4. Create a controller
Minimal — zero custom code:
@CrudController<PostEntity, EM>({ model: PostEntity })export class PostsController extends RelayerController<PostEntity, EM> { constructor(@InjectQueryService(PostEntity) service: RelayerService<PostEntity, EM>) { super(service); }}That’s it. You now have:
GET /posts -- list with filtering, search, paginationGET /posts/:id -- get by idPOST /posts -- createPATCH /posts/:id -- updateDELETE /posts/:id -- deleteGET /posts/count -- count with filtering5. Feature module
@Module({ imports: [RelayerModule.forFeature([PostEntity])], controllers: [PostsController],})export class PostsModule {}What’s next
API Reference:
- CRUD Controller — routes, defaults, access control, decorators, pagination
- Query Service — service methods, defaults, cross-entity access, DI
- Hooks — lifecycle hooks for side effects
- Data Mapper — transform response shapes
Usage:
- Search & Filtering — how clients query your API
- Aggregations — groupBy, count, sum, avg, min, max
- Validation — Zod schemas, class-validator DTOs