// NestJS Complete Application Template
import {
Controller, Get, Post, Put, Delete, Body, Param, Query,
UseGuards, Injectable, Module, NestFactory
} from '@nestjs/common';
import {
ApiTags, ApiOperation, ApiResponse, ApiBearerAuth,
ApiQuery, ApiParam
} from '@nestjs/swagger';
import { IsString, IsEmail, IsOptional, IsEnum, MinLength } from 'class-validator';
import { JwtAuthGuard } from './auth/jwt-auth.guard';
// ============================================
// DTOs (Data Transfer Objects)
// ============================================
export class CreateUserDto {
@IsEmail()
email: string;
@IsString()
@MinLength(2)
name: string;
@IsString()
@MinLength(8)
password: string;
}
export class UpdateUserDto {
@IsOptional()
@IsString()
name?: string;
@IsOptional()
@IsString()
bio?: string;
}
export class CreatePostDto {
@IsString()
@MinLength(1)
title: string;
@IsString()
content: string;
@IsOptional()
@IsEnum(['draft', 'published'])
status?: 'draft' | 'published';
}
export class QueryPostDto {
@IsOptional()
@IsString()
search?: string;
@IsOptional()
@IsEnum(['draft', 'published'])
status?: 'draft' | 'published';
@IsOptional()
limit?: number = 20;
@IsOptional()
offset?: number = 0;
}
// ============================================
// Entities
// ============================================
export interface User {
id: string;
email: string;
name: string;
bio?: string;
createdAt: Date;
updatedAt: Date;
}
export interface Post {
id: string;
title: string;
content: string;
authorId: string;
status: 'draft' | 'published';
views: number;
createdAt: Date;
publishedAt?: Date;
}
// ============================================
// Services
// ============================================
@Injectable()
export class UsersService {
private users: Map<string, User> = new Map();
private idCounter = 1;
async findAll(): Promise<User[]> {
return Array.from(this.users.values());
}
async findById(id: string): Promise<User | undefined> {
return this.users.get(id);
}
async findByEmail(email: string): Promise<User | undefined> {
return Array.from(this.users.values()).find(u => u.email === email);
}
async create(dto: CreateUserDto): Promise<User> {
const id = String(this.idCounter++);
const user: User = {
id,
email: dto.email,
name: dto.name,
createdAt: new Date(),
updatedAt: new Date(),
};
this.users.set(id, user);
return user;
}
async update(id: string, dto: UpdateUserDto): Promise<User | undefined> {
const user = this.users.get(id);
if (!user) return undefined;
const updated = { ...user, ...dto, updatedAt: new Date() };
this.users.set(id, updated);
return updated;
}
async delete(id: string): Promise<boolean> {
return this.users.delete(id);
}
}
@Injectable()
export class PostsService {
private posts: Map<string, Post> = new Map();
private idCounter = 1;
async findAll(query: QueryPostDto): Promise<{ data: Post[]; total: number }> {
let posts = Array.from(this.posts.values());
if (query.status) {
posts = posts.filter(p => p.status === query.status);
}
if (query.search) {
const search = query.search.toLowerCase();
posts = posts.filter(p =>
p.title.toLowerCase().includes(search) ||
p.content.toLowerCase().includes(search)
);
}
const total = posts.length;
const data = posts.slice(query.offset, query.offset! + query.limit!);
return { data, total };
}
async findById(id: string): Promise<Post | undefined> {
return this.posts.get(id);
}
async create(dto: CreatePostDto, authorId: string): Promise<Post> {
const id = String(this.idCounter++);
const post: Post = {
id,
title: dto.title,
content: dto.content,
authorId,
status: dto.status || 'draft',
views: 0,
createdAt: new Date(),
};
this.posts.set(id, post);
return post;
}
async publish(id: string): Promise<Post | undefined> {
const post = this.posts.get(id);
if (!post) return undefined;
post.status = 'published';
post.publishedAt = new Date();
return post;
}
async incrementViews(id: string): Promise<number> {
const post = this.posts.get(id);
if (!post) return 0;
post.views++;
return post.views;
}
async delete(id: string): Promise<boolean> {
return this.posts.delete(id);
}
}
// ============================================
// Controllers
// ============================================
@ApiTags('users')
@Controller('users')
export class UsersController {
constructor(private readonly usersService: UsersService) { }
@Get()
@ApiOperation({ summary: 'Get all users' })
@ApiResponse({ status: 200, description: 'List of users' })
async findAll(): Promise<User[]> {
return this.usersService.findAll();
}
@Get(':id')
@ApiOperation({ summary: 'Get user by ID' })
@ApiParam({ name: 'id', description: 'User ID' })
@ApiResponse({ status: 200, description: 'User found' })
@ApiResponse({ status: 404, description: 'User not found' })
async findOne(@Param('id') id: string): Promise<User | undefined> {
return this.usersService.findById(id);
}
@Post()
@ApiOperation({ summary: 'Create a new user' })
@ApiResponse({ status: 201, description: 'User created' })
async create(@Body() dto: CreateUserDto): Promise<User> {
return this.usersService.create(dto);
}
@Put(':id')
@UseGuards(JwtAuthGuard)
@ApiBearerAuth()
@ApiOperation({ summary: 'Update user' })
async update(@Param('id') id: string, @Body() dto: UpdateUserDto): Promise<User | undefined> {
return this.usersService.update(id, dto);
}
@Delete(':id')
@UseGuards(JwtAuthGuard)
@ApiBearerAuth()
@ApiOperation({ summary: 'Delete user' })
async delete(@Param('id') id: string): Promise<{ deleted: boolean }> {
const deleted = await this.usersService.delete(id);
return { deleted };
}
}
@ApiTags('posts')
@Controller('posts')
export class PostsController {
constructor(private readonly postsService: PostsService) { }
@Get()
@ApiOperation({ summary: 'Get all posts' })
@ApiQuery({ name: 'search', required: false })
@ApiQuery({ name: 'status', required: false, enum: ['draft', 'published'] })
async findAll(@Query() query: QueryPostDto) {
return this.postsService.findAll(query);
}
@Get(':id')
@ApiOperation({ summary: 'Get post by ID' })
async findOne(@Param('id') id: string) {
const post = await this.postsService.findById(id);
if (post) {
await this.postsService.incrementViews(id);
}
return post;
}
@Post()
@UseGuards(JwtAuthGuard)
@ApiBearerAuth()
@ApiOperation({ summary: 'Create a new post' })
async create(@Body() dto: CreatePostDto) {
// In real app, get authorId from request user
return this.postsService.create(dto, 'current-user-id');
}
@Post(':id/publish')
@UseGuards(JwtAuthGuard)
@ApiBearerAuth()
@ApiOperation({ summary: 'Publish a post' })
async publish(@Param('id') id: string) {
return this.postsService.publish(id);
}
@Delete(':id')
@UseGuards(JwtAuthGuard)
@ApiBearerAuth()
async delete(@Param('id') id: string) {
return { deleted: await this.postsService.delete(id) };
}
}
// ============================================
// Module
// ============================================
@Module({
controllers: [UsersController, PostsController],
providers: [UsersService, PostsService],
exports: [UsersService, PostsService],
})
export class AppModule { }
// ============================================
// Bootstrap
// ============================================
async function bootstrap() {
const app = await NestFactory.create(AppModule);
// Enable CORS
app.enableCors();
// Global prefix
app.setGlobalPrefix('api');
// Swagger setup would go here
await app.listen(3000);
console.log('Application running on http://localhost:3000');
}
// bootstrap();