Skip to main content

Generator

Rhino includes an interactive CLI that scaffolds resource stubs with the Rhino conventions in place. Generate a model stub, a policy, a scope, or a request class in seconds.

Usage​

terminal
npx rhino generate

The command launches an interactive prompt:

  Rhino :: Generate

? What would you like to generate? (model | policy | scope | request)
? Name (PascalCase, e.g. "BlogPost"):

What It Generates​

The generator can create four types of stubs:

Model​

Generates a @RhinoModel()-decorated class stub. You are prompted for the columns (comma-separated name:type pairs):

? Columns (comma-separated, e.g. "title:string, body:text, published:boolean"):

Generated file: src/models/{Name}.model.ts

src/models/BlogPost.model.ts
/**
* BlogPost model stub — generated by `npx rhino generate`
* Register in rhino.config.ts:
* blogPosts: { model: 'BlogPost' }
*/
import { RhinoModel } from '@rhino-dev/rhino-nestjs';

@RhinoModel()
export class BlogPost {
id!: number;
title?: string;
body?: string;
published?: boolean;
createdAt?: Date;
updatedAt?: Date;
}
Decorator vs config registration

The @RhinoModel() decorator is an optional alternative to registering the model directly in src/rhino.config.ts. The config-based models: { slug: { model: '...' } } approach is the primary path; see Models. The generator does not add the entry to your config — register the new model yourself (the stub's header comment shows the line to add).

Policy​

Generates a policy class that extends ResourcePolicy:

Generated file: src/policies/{Name}.policy.ts

src/policies/Post.policy.ts
/**
* Post policy stub — generated by `npx rhino generate`
* Extend ResourcePolicy and override the methods you need.
*/
import { ResourcePolicy } from '@rhino-dev/rhino-nestjs';

export class PostPolicy extends ResourcePolicy {
// override viewAny(user: any, org?: any): boolean { ... }
// override view(user: any, record: any, org?: any): boolean { ... }
// override create(user: any, org?: any): boolean { ... }
// override update(user: any, record: any, org?: any): boolean { ... }
// override delete(user: any, record: any, org?: any): boolean { ... }
}

Scope​

Generates a scope class implementing RhinoScope:

Generated file: src/scopes/{Name}.scope.ts

src/scopes/Post.scope.ts
/**
* Post scope stub — generated by `npx rhino generate`
* Implement the `apply` method to filter / order the default query.
*/
import type { RhinoScope } from '@rhino-dev/rhino-nestjs';

export class PostScope implements RhinoScope {
apply(query: Record<string, any>, _context: any): Record<string, any> {
// Example: return { ...query, where: { ...query.where, deletedAt: null } };
return query;
}
}

Request​

Generates a request class — the validation for one write action on one model. You are asked which action(s) to generate:

? Which action(s)? (store | update | both)

Generated file: src/requests/{name}-{action}.request.ts

src/requests/post-store.request.ts
/**
* PostStoreRequest — generated by `npx rhino generate`
*
* IMPORTANT: the parsed output IS the write payload. A field with no rule in
* `rules()` is silently dropped and never persisted — declare every field you
* want written, including optional ones.
*/
import { z } from 'zod';
import {
ResourceRequest,
type ResourceRequestContext,
} from '@rhino-dev/rhino-nestjs';

export class PostStoreRequest extends ResourceRequest {
rules(ctx: ResourceRequestContext) {
return z.object({
// title: z.string().min(1).max(255),
});
}
}

The real stub is heavily commented: it shows authorize() and prepare(), a role-dependent rule, a route-group-dependent rule and — on the update class — a rule that reads ctx.record. After writing the files the generator prints the registration line to paste into src/rhino.config.ts:

  Register on the model:
posts: { model: 'Post', requests: { store: PostStoreRequest, update: PostUpdateRequest } }

Unlike Laravel and Rails, NestJS has no filesystem discovery — a request class runs only once it is registered.

Interactive Walkthrough​

Step 1: Choose what to generate​

? What would you like to generate?
> model
policy
scope
request

Step 2: Enter the name​

? Name (PascalCase, e.g. "BlogPost"): Post

Use PascalCase singular (e.g., Post, not posts). The generator derives the file name from the name.

Step 3 (model only): Define columns​

? Columns (comma-separated, e.g. "title:string, body:text, published:boolean"): title:string, body:text

Each column is a name:type pair. Recognized types map to TypeScript types in the generated stub:

Column typeTypeScript type
string, text, uuidstring
integer, bigInteger, decimal, floatnumber
booleanboolean
date, datetime, timestampDate

If a stub file already exists at the target path, the generator skips it rather than overwriting.

After Generation​

The generator only writes a stub. To wire it up:

  1. Add the column(s) to your prisma/schema.prisma model and run npx prisma migrate dev.
  2. Register the model in src/rhino.config.ts (the stub header comment shows the entry to add).
  3. Attach the policy, scope and/or request classes to the registration via policy:, scopes: and requests:.

For end-to-end generation (Prisma models, policies, tests, and seeders all at once), see the Blueprint system.