NestJS: Known Limitations
Relation row limits ($limit)
When you use $limit to cap relation rows, the strategy depends on what fields are in the nested select.
Scalar fields only — SQL-level limiting
If your relation select contains only scalar columns (no computed or derived fields), Relayer uses ROW_NUMBER() in SQL:
SELECT "id", "content", "post_id", "author_id"FROM ( SELECT *, ROW_NUMBER() OVER (PARTITION BY "post_id") as "__rn" FROM "comments" WHERE "post_id" IN (1, 2, 3)) "__sub"WHERE "__rn" <= 5This is efficient — the database handles the per-parent limiting, only the needed rows are returned.
Computed or derived fields — JS-level limiting
When the relation select includes computed fields (@Entity.computed) or derived fields (@Entity.derived), SQL-level limiting is not possible — these fields require Drizzle’s query builder with custom SQL expressions that can’t be wrapped in a ROW_NUMBER() subquery.
In this case, Relayer falls back to loading all matching rows from the database and slicing per-parent in JavaScript:
// All comments loaded, then sliced to 5 per post in JSselect: { comments: { $limit: 5, id: true, content: true, upperContent: true } // ^^^^^^^^^^^^ // computed field -> JS fallback}Performance impact: For large datasets (thousands of child records per parent), the JS fallback loads all rows into memory before slicing. This can cause:
- High memory usage
- Slow response times
- Database load from transferring unnecessary rows
Recommendations:
- Avoid computed/derived fields in limited relation selects when working with large datasets
- Use a nested resource endpoint instead:
GET /posts/5/comments?limit=5runs a direct query with SQL LIMIT - Set
defaultRelationLimitglobally to prevent accidental full-table loads
SQLite
SQLite (better-sqlite3) always uses JS-level limiting regardless of field types, because the driver lacks async execute() support for raw SQL.
Entity types and relations
Entity classes created with createRelayerEntity(schema, 'posts') include scalar and computed/derived fields but not relation fields by default.
To get full relation-aware types (with autocomplete for entity.comments, entity.author.fullName, etc.), use the entity map pattern with TEntities generic:
// Define entity mapexport const entities = { users: UserEntity, posts: PostEntity, comments: CommentEntity };export type EM = typeof entities;
// Service with relation-aware typesclass PostsService extends RelayerService<PostEntity, EM> { ... }
// Model<PostEntity, EM> includes: id, title, ..., author, comments (with nested types)Model<TEntity, TEntities> resolves relation fields automatically from the Drizzle schema. This works in services, hooks, dto mappers, and controller config (via @CrudController<PostEntity, EM>).
Roadmap
- Better integration with Relayer context object