NestJS Support
LogicStamp Context provides comprehensive support for NestJS applications, automatically detecting controllers, routes, decorators, and extracting API signatures from your backend code.
NestJS Detection
LogicStamp automatically identifies NestJS code by:
- NestJS imports: Detects imports from
@nestjs/common,@nestjs/core, and other@nestjs/*packages - Controller decorators: Recognizes
@Controller()decorator on classes - Route decorators: Detects
@Get(),@Post(),@Put(),@Delete(),@Patch()decorators on methods - Class-based architecture: Identifies controller classes and their methods
Detection requires both:
- NestJS import (
import { Controller } from '@nestjs/common') - Controller or route decorators (
@Controller(),@Get(), etc.)
This two-factor detection prevents false positives - files that import NestJS but don't define controllers won't be detected as backend files.
What Gets Extracted
Controllers
NestJS controllers are automatically detected and analyzed:
// Example: users.controller.ts
import { Controller, Get, Post, Body, Param } from '@nestjs/common';
@Controller('users')
export class UsersController {
@Get()
findAll() {
return [];
}
@Get(':id')
findOne(@Param('id') id: string) {
return { id };
}
@Post()
create(@Body() createUserDto: CreateUserDto) {
return { id: '123', ...createUserDto };
}
}Extracted information:
- Controller name:
UsersController - Base path:
users(from@Controller('users')) - Routes: All methods with HTTP decorators
- Route paths: Extracted from decorator arguments
- Route parameters: Extracted from path patterns (e.g.,
:id→['id'])
Route Methods
LogicStamp extracts routes from controller methods:
@Controller('users')
export class UsersController {
@Get() // Route: GET /users
findAll() { }
@Get(':id') // Route: GET /users/:id
findOne(@Param('id') id: string) { }
@Post() // Route: POST /users
create(@Body() dto: CreateUserDto) { }
@Put(':id') // Route: PUT /users/:id
update(@Param('id') id: string, @Body() dto: UpdateUserDto) { }
@Delete(':id') // Route: DELETE /users/:id
remove(@Param('id') id: string) { }
@Patch(':id') // Route: PATCH /users/:id
patch(@Param('id') id: string, @Body() dto: PatchUserDto) { }
}Extracted routes:
- HTTP methods: GET, POST, PUT, DELETE, PATCH
- Route paths: Combined base path + method path
- Handler names: Method names (
findAll,findOne, etc.) - Route parameters: Extracted from path patterns
Base Path Extraction
The controller base path is extracted from the @Controller() decorator:
// With base path
@Controller('users')
export class UsersController {
@Get() // Route: GET /users
findAll() { }
}
// Extracted: basePath: 'users'
// Without base path
@Controller()
export class UsersController {
@Get() // Route: GET /
findAll() { }
}
// Extracted: basePath: undefined
// Nested path
@Controller('api/v1/users')
export class UsersController {
@Get() // Route: GET /api/v1/users
findAll() { }
}
// Extracted: basePath: 'api/v1/users'API Signatures
LogicStamp extracts API signatures from controller methods:
import { Controller, Get, Post, Body, Param } from '@nestjs/common';
interface CreateUserDto {
name: string;
email: string;
}
interface User {
id: string;
name: string;
email: string;
}
@Controller('users')
export class UsersController {
@Get(':id')
findOne(@Param('id') id: string): Promise<User> {
// ...
}
@Post()
create(@Body() createUserDto: CreateUserDto): Promise<User> {
// ...
}
}Extracted API signature:
- Parameters:
{ id: 'string', createUserDto: 'CreateUserDto' } - Return type:
Promise<User> - Request type:
CreateUserDto(for POST/PUT/PATCH) - Response type:
User
Decorators and Annotations
LogicStamp extracts NestJS decorators for language-specific metadata:
import {
Controller,
Get,
Post,
Put,
Delete,
Patch,
Body,
Param,
Query,
Headers
} from '@nestjs/common';
@Controller('users')
export class UsersController {
@Get()
findAll() { }
@Post()
create(@Body() dto: CreateUserDto) { }
}Extracted language-specific metadata:
- Annotations:
['@Controller', '@Get', '@Post', '@Body'] - Classes:
['UsersController'] - Methods:
['findAll', 'create']
Component Kinds
LogicStamp categorizes NestJS files into different kinds:
node:api- NestJS controller files with route definitionsts:module- TypeScript modules/utilities (non-controller files)
Routes and tsc
The same idea applies as with Express: changing @Controller('users') to @Controller('user'), or a route segment on @Get() / @Post(), can leave method signatures and DTO types unchanged so tsc stays quiet, while the public HTTP surface changes. Those paths feed the extracted contract and semanticHash, so stamp context compare can show the change. Strict violation rules for HTTP are narrower; see API signature handling.
NestJS-Specific Features
Controller Detection
LogicStamp detects controllers via the @Controller() decorator:
// Single controller per file (recommended)
@Controller('users')
export class UsersController {
// ...
}
// Multiple controllers (detected separately)
@Controller('users')
export class UsersController {
// ...
}
@Controller('posts')
export class PostsController {
// ...
}Note: If multiple controllers exist in one file, only the first one with @Controller() is extracted.
Route Path Extraction
Route paths are extracted from decorator arguments:
@Controller('users')
export class UsersController {
@Get() // Path: '' (empty, uses base path)
findAll() { }
@Get(':id') // Path: ':id'
findOne() { }
@Get('profile') // Path: 'profile' (full route: GET /users/profile)
getProfile() { }
@Get('profile/:id') // Path: 'profile/:id'
getProfileById() { }
}Route Parameters
LogicStamp extracts route parameters from path patterns:
@Controller('users')
export class UsersController {
@Get(':id') // params: ['id']
findOne() { }
@Get(':userId/posts/:postId') // params: ['userId', 'postId']
getUserPost() { }
}Method Parameter Extraction
Controller method parameters are extracted with their types:
@Controller('users')
export class UsersController {
@Get(':id')
findOne(
@Param('id') id: string,
@Query('include') include?: string
): Promise<User> {
// ...
}
@Post()
create(
@Body() createUserDto: CreateUserDto,
@Headers('authorization') auth: string
): Promise<User> {
// ...
}
}Extracted parameters:
id: string(from@Param('id'))include?: string(from@Query('include'))createUserDto: CreateUserDto(from@Body())auth: string(from@Headers('authorization'))
Return Type Extraction
Return types are extracted from method signatures:
@Controller('users')
export class UsersController {
@Get()
findAll(): User[] {
return [];
}
@Get(':id')
async findOne(@Param('id') id: string): Promise<User> {
return {};
}
@Post()
create(@Body() dto: CreateUserDto): Observable<User> {
return of({});
}
}Extracted return types:
User[]- Array return typePromise<User>- Promise return typeObservable<User>- Observable return type
Usage
Analyzing NestJS Projects
Generate context for a NestJS project:
stamp contextThe tool will automatically:
- Detect NestJS imports and controller decorators
- Extract controller classes and base paths
- Extract route definitions (paths, methods, handlers)
- Extract API signatures from controller methods
- Identify route parameters
- Extract decorators and annotations
- Generate structured contracts for each controller file
Project Structure
LogicStamp works with common NestJS project structures:
my-nestjs-app/
src/
users/
users.controller.ts # Controller with routes
users.service.ts # Service (not extracted)
users.module.ts # Module (not extracted)
posts/
posts.controller.ts
app.module.ts # Root module
main.ts # Application entry pointIntegration with Build Tools
LogicStamp works with common NestJS setups:
- NestJS CLI - Full support for NestJS CLI projects
- TypeScript - Full support for TypeScript NestJS projects
- Decorators - All NestJS decorators are detected
- Custom setups - Compatible with any NestJS TypeScript project
Best Practices
1. One Controller Per File
Keep controllers separate for better extraction:
// ✅ Good: Single controller per file
// users.controller.ts
@Controller('users')
export class UsersController {
// ...
}
// ❌ Less ideal: Multiple controllers in one file
// controllers.ts
@Controller('users')
export class UsersController { }
@Controller('posts')
export class PostsController { }2. Type Your DTOs
TypeScript DTOs improve extraction quality:
// ✅ Good: Typed DTOs
export class CreateUserDto {
name: string;
email: string;
}
@Post()
create(@Body() createUserDto: CreateUserDto): Promise<User> {
// ...
}
// ❌ Less ideal: Any type
@Post()
create(@Body() body: any): Promise<any> {
// ...
}3. Use Explicit Return Types
Explicit return types improve API signature extraction:
// ✅ Good: Explicit return type
@Get(':id')
findOne(@Param('id') id: string): Promise<User> {
// ...
}
// ❌ Less ideal: Inferred return type
@Get(':id')
findOne(@Param('id') id: string) {
// ...
}4. Organize Controllers by Resource
Keep related routes together:
src/
users/
users.controller.ts # User routes
users.service.ts
users.module.ts
posts/
posts.controller.ts # Post routes
posts.service.ts
posts.module.tsExamples
NestJS Controller
// users.controller.ts
import {
Controller,
Get,
Post,
Put,
Delete,
Body,
Param,
Query
} from '@nestjs/common';
interface CreateUserDto {
name: string;
email: string;
}
interface UpdateUserDto {
name?: string;
email?: string;
}
interface User {
id: string;
name: string;
email: string;
}
@Controller('users')
export class UsersController {
@Get()
findAll(@Query('limit') limit?: number): Promise<User[]> {
return Promise.resolve([]);
}
@Get(':id')
findOne(@Param('id') id: string): Promise<User> {
return Promise.resolve({ id, name: '', email: '' });
}
@Post()
create(@Body() createUserDto: CreateUserDto): Promise<User> {
return Promise.resolve({ id: '123', ...createUserDto });
}
@Put(':id')
update(
@Param('id') id: string,
@Body() updateUserDto: UpdateUserDto
): Promise<User> {
return Promise.resolve({ id, ...updateUserDto });
}
@Delete(':id')
remove(@Param('id') id: string): Promise<void> {
return Promise.resolve();
}
}Generated Contract:
{
"kind": "node:api",
"composition": {
"variables": [],
"hooks": [],
"components": [],
"functions": ["findAll", "findOne", "create", "update", "remove"],
"languageSpecific": {
"annotations": [
"@Controller",
"@Get",
"@Post",
"@Put",
"@Delete",
"@Body",
"@Param",
"@Query"
],
"classes": ["UsersController"],
"methods": ["findAll", "findOne", "create", "update", "remove"]
}
},
"interface": {
"props": {},
"emits": {},
"apiSignature": {
"parameters": {
"id": "string",
"limit": "number",
"createUserDto": "CreateUserDto",
"updateUserDto": "UpdateUserDto"
},
"returnType": "Promise<User | User[] | void>",
"requestType": "CreateUserDto | UpdateUserDto",
"responseType": "User | User[]"
}
},
"backend": {
"framework": "nestjs",
"controller": {
"name": "UsersController",
"basePath": "users"
},
"routes": [
{
"path": "",
"method": "GET",
"handler": "findAll",
"params": []
},
{
"path": ":id",
"method": "GET",
"handler": "findOne",
"params": ["id"]
},
{
"path": "",
"method": "POST",
"handler": "create",
"params": []
},
{
"path": ":id",
"method": "PUT",
"handler": "update",
"params": ["id"]
},
{
"path": ":id",
"method": "DELETE",
"handler": "remove",
"params": ["id"]
}
],
"languageSpecific": {
"annotations": [
"@Controller",
"@Get",
"@Post",
"@Put",
"@Delete",
"@Body",
"@Param",
"@Query"
],
"classes": ["UsersController"],
"methods": ["findAll", "findOne", "create", "update", "remove"]
}
}
}NestJS Controller with Guards and Interceptors
import { Controller, Get, UseGuards, UseInterceptors } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
import { LoggingInterceptor } from './logging.interceptor';
@Controller('users')
@UseGuards(AuthGuard('jwt'))
@UseInterceptors(LoggingInterceptor)
export class UsersController {
@Get()
findAll() {
return [];
}
}Extracted:
- Controller:
UsersControllerwith base pathusers - Routes:
GET /users - Decorators:
@Controller,@Get,@UseGuards,@UseInterceptors - Note: Guards and interceptors are detected as decorators but their logic is not extracted
Framework Detection Priority
When a file imports multiple frameworks, LogicStamp uses a priority system:
- Backend (Express/NestJS detected) →
node:api(highest priority) - Vue (Vue import + Vue component patterns) →
vue:componentorvue:composable - React (React import or JSX/TSX + React patterns) →
react:componentorreact:hook - TypeScript module (no framework patterns) →
ts:module(default fallback)
Note: React is NOT the default fallback. Files without framework patterns become ts:module.
Example:
import { Controller } from '@nestjs/common';
import { useState } from 'react'; // This file will be analyzed as backend
@Controller('api')
export class ApiController {
// Analyzed as node:api, not react:component
}Result:
- Backend controller: ✅ Extracted (
backend.controller,backend.routes) - React hooks: ❌ Not extracted (skipped when backend detected)
- React components: ❌ Not extracted (skipped when backend detected)
- Contract kind:
node:api
Recommendation: Keep backend and frontend code in separate files for accurate analysis. LogicStamp analyzes each file independently, so separating concerns ensures both are extracted correctly in their respective files.
Limitations
Services and Modules
NestJS services and modules are not extracted:
// ✅ Controllers are extracted
@Controller('users')
export class UsersController { }
// ❌ Services are not extracted
@Injectable()
export class UsersService { }
// ❌ Modules are not extracted
@Module({ })
export class UsersModule { }Why: LogicStamp focuses on extracting API routes and their signatures. Controllers define the API surface, while services and modules are implementation details that don't directly affect the API contract.
Impact:
- Controllers and routes are fully extracted
- Service methods and business logic are not documented
- Module configuration and dependency injection setup are not tracked
Workaround: Document services and modules separately if needed. LogicStamp focuses on the API contract (controllers and routes).
Guards and Interceptors
Guards and interceptors are detected as decorators but their logic is not extracted:
// ✅ Decorator is detected
@UseGuards(AuthGuard('jwt'))
export class UsersController { }
// ❌ Guard logic is not extracted
@Injectable()
export class AuthGuard implements CanActivate {
canActivate() { }
}Why: LogicStamp extracts decorators as metadata (they appear in languageSpecific.annotations), but doesn't analyze the guard/interceptor implementation. Extracting guard logic would require analyzing multiple files and understanding NestJS's dependency injection system.
Impact:
- Decorators are detected:
@UseGuards,@UseInterceptorsappear in annotations - Guard/interceptor classes are not analyzed
- Guard logic and conditions are not extracted
Example:
@Controller('users')
@UseGuards(AuthGuard('jwt')) // ✅ Detected as decorator
export class UsersController {
@Get()
findAll() { }
}
// Extracted: annotations: ['@Controller', '@UseGuards', '@Get']
// But AuthGuard implementation is not analyzedWorkaround: Guards and interceptors are documented as decorators. Their implementation can be documented separately if needed.
Pipes and Filters
Pipes and filters are not extracted:
// ❌ Pipes are not extracted
@Injectable()
export class ValidationPipe implements PipeTransform { }
// ❌ Filters are not extracted
@Catch(HttpException)
export class HttpExceptionFilter { }Why: Similar to guards and interceptors, pipes and filters are implementation details that don't directly define the API contract. Extracting them would require analyzing multiple files and understanding NestJS's execution context.
Impact:
- Pipes used in route handlers (e.g.,
@Body(ValidationPipe)) are detected as decorators - Pipe implementations are not analyzed
- Exception filters are not extracted
Workaround: Document pipes and filters separately. LogicStamp focuses on the API contract (routes, parameters, return types).
Dynamic Route Paths
Only string literal paths are extracted:
// ✅ Extracted: String literal
@Get(':id')
findOne() { }
// ❌ Not extracted: Dynamic path construction
const path = ':id';
@Get(path)
findOne() { }Why: LogicStamp uses static analysis and can only extract values that are known at compile time. Dynamic paths require runtime evaluation, which static analysis cannot perform.
Impact:
- String literal paths work perfectly:
@Get(':id') - Variable paths are not extracted:
const path = ':id'; @Get(path) - Template literals with variables:
@Get(`${prefix}/:id`) - Function calls:
@Get(getRoutePath())
Workaround: Use string literals for route paths when possible. If you need dynamic paths, they won't be extracted, but the route structure is still captured.
Multiple Controllers
If multiple controllers exist in one file, only the first one is extracted:
// ⚠️ Only UsersController is extracted
@Controller('users')
export class UsersController { }
@Controller('posts')
export class PostsController { }Why: The extractor finds the first class with a @Controller() decorator and stops. NestJS best practices recommend one controller per file, so this limitation aligns with common patterns.
Impact:
- First controller in file is fully extracted
- Subsequent controllers are ignored
- Routes from multiple controllers in one file are not all captured
Recommendation: Keep one controller per file for best results. This follows NestJS best practices and ensures all controllers are extracted correctly.
Example:
// users.controller.ts - ✅ Fully extracted
@Controller('users')
export class UsersController {
@Get()
findAll() { }
}
// posts.controller.ts - ✅ Fully extracted (separate file)
@Controller('posts')
export class PostsController {
@Get()
findAll() { }
}
// ❌ Don't do this - only first controller extracted
// controllers.ts
@Controller('users')
export class UsersController { } // ✅ Extracted
@Controller('posts')
export class PostsController { } // ❌ Not extractedRelated Documentation
- Express.js Support - Express.js framework features
- TypeScript Support - TypeScript-specific features
- CLI Reference - Command-line usage
- Contract Schema - UIFContract format
NestJS Ecosystem Support
LogicStamp works with popular NestJS ecosystem libraries:
- NestJS Common - Core decorators and utilities
- NestJS Core - Framework core functionality
- NestJS Passport - Authentication (decorators detected)
- NestJS TypeORM - Database integration (decorators detected)
- NestJS Swagger - API documentation (decorators detected)
For best results, structure your NestJS project with TypeScript, use explicit types for DTOs and return types, and keep one controller per file.
Next Steps
Explore other frameworks or learn about the CLI.