Skip to content

Full-Stack Nest + Next.js Example

Great question! Here’s how your NestJS backend would look compared to the FastAPI structure. NestJS follows a more Angular-inspired, decorator-based architecture:

🟒 NestJS Backend Structure

backend/
β”œβ”€β”€ src/
β”‚ β”œβ”€β”€ main.ts # NestJS app entry point
β”‚ β”œβ”€β”€ app.module.ts # Root module
β”‚ β”œβ”€β”€ app.controller.ts # Root controller
β”‚ β”œβ”€β”€ app.service.ts # Root service
β”‚ β”œβ”€β”€ config/ # Configuration
β”‚ β”‚ β”œβ”€β”€ database.config.ts
β”‚ β”‚ β”œβ”€β”€ jwt.config.ts
β”‚ β”‚ └── app.config.ts
β”‚ β”œβ”€β”€ modules/ # Feature modules
β”‚ β”‚ β”œβ”€β”€ users/
β”‚ β”‚ β”‚ β”œβ”€β”€ users.module.ts
β”‚ β”‚ β”‚ β”œβ”€β”€ users.controller.ts
β”‚ β”‚ β”‚ β”œβ”€β”€ users.service.ts
β”‚ β”‚ β”‚ β”œβ”€β”€ users.repository.ts
β”‚ β”‚ β”‚ β”œβ”€β”€ dto/
β”‚ β”‚ β”‚ β”‚ β”œβ”€β”€ create-user.dto.ts
β”‚ β”‚ β”‚ β”‚ β”œβ”€β”€ update-user.dto.ts
β”‚ β”‚ β”‚ β”‚ └── user-response.dto.ts
β”‚ β”‚ β”‚ β”œβ”€β”€ entities/
β”‚ β”‚ β”‚ β”‚ └── user.entity.ts
β”‚ β”‚ β”‚ └── users.controller.spec.ts
β”‚ β”‚ β”œβ”€β”€ projects/
β”‚ β”‚ β”‚ β”œβ”€β”€ projects.module.ts
β”‚ β”‚ β”‚ β”œβ”€β”€ projects.controller.ts
β”‚ β”‚ β”‚ β”œβ”€β”€ projects.service.ts
β”‚ β”‚ β”‚ β”œβ”€β”€ projects.repository.ts
β”‚ β”‚ β”‚ β”œβ”€β”€ dto/
β”‚ β”‚ β”‚ β”‚ β”œβ”€β”€ create-project.dto.ts
β”‚ β”‚ β”‚ β”‚ β”œβ”€β”€ update-project.dto.ts
β”‚ β”‚ β”‚ β”‚ └── project-response.dto.ts
β”‚ β”‚ β”‚ β”œβ”€β”€ entities/
β”‚ β”‚ β”‚ β”‚ └── project.entity.ts
β”‚ β”‚ β”‚ └── projects.controller.spec.ts
β”‚ β”‚ β”œβ”€β”€ soq/
β”‚ β”‚ β”‚ β”œβ”€β”€ soq.module.ts
β”‚ β”‚ β”‚ β”œβ”€β”€ soq.controller.ts
β”‚ β”‚ β”‚ β”œβ”€β”€ soq.service.ts
β”‚ β”‚ β”‚ β”œβ”€β”€ soq.repository.ts
β”‚ β”‚ β”‚ β”œβ”€β”€ dto/
β”‚ β”‚ β”‚ β”‚ β”œβ”€β”€ create-soq.dto.ts
β”‚ β”‚ β”‚ β”‚ β”œβ”€β”€ update-soq.dto.ts
β”‚ β”‚ β”‚ β”‚ └── soq-response.dto.ts
β”‚ β”‚ β”‚ β”œβ”€β”€ entities/
β”‚ β”‚ β”‚ β”‚ └── soq.entity.ts
β”‚ β”‚ β”‚ └── soq.controller.spec.ts
β”‚ β”‚ β”œβ”€β”€ tenders/
β”‚ β”‚ β”‚ β”œβ”€β”€ tenders.module.ts
β”‚ β”‚ β”‚ β”œβ”€β”€ tenders.controller.ts
β”‚ β”‚ β”‚ β”œβ”€β”€ tenders.service.ts
β”‚ β”‚ β”‚ β”œβ”€β”€ tenders.repository.ts
β”‚ β”‚ β”‚ β”œβ”€β”€ dto/
β”‚ β”‚ β”‚ β”‚ β”œβ”€β”€ create-tender.dto.ts
β”‚ β”‚ β”‚ β”‚ β”œβ”€β”€ update-tender.dto.ts
β”‚ β”‚ β”‚ β”‚ └── tender-response.dto.ts
β”‚ β”‚ β”‚ β”œβ”€β”€ entities/
β”‚ β”‚ β”‚ β”‚ └── tender.entity.ts
β”‚ β”‚ β”‚ └── tenders.controller.spec.ts
β”‚ β”‚ β”œβ”€β”€ escrow/
β”‚ β”‚ β”‚ β”œβ”€β”€ escrow.module.ts
β”‚ β”‚ β”‚ β”œβ”€β”€ escrow.controller.ts
β”‚ β”‚ β”‚ β”œβ”€β”€ escrow.service.ts
β”‚ β”‚ β”‚ β”œβ”€β”€ escrow.repository.ts
β”‚ β”‚ β”‚ β”œβ”€β”€ dto/
β”‚ β”‚ β”‚ β”‚ β”œβ”€β”€ create-escrow.dto.ts
β”‚ β”‚ β”‚ β”‚ β”œβ”€β”€ update-escrow.dto.ts
β”‚ β”‚ β”‚ β”‚ └── escrow-response.dto.ts
β”‚ β”‚ β”‚ β”œβ”€β”€ entities/
β”‚ β”‚ β”‚ β”‚ └── escrow.entity.ts
β”‚ β”‚ β”‚ └── escrow.controller.spec.ts
β”‚ β”‚ └── audit/
β”‚ β”‚ β”œβ”€β”€ audit.module.ts
β”‚ β”‚ β”œβ”€β”€ audit.controller.ts
β”‚ β”‚ β”œβ”€β”€ audit.service.ts
β”‚ β”‚ β”œβ”€β”€ audit.repository.ts
β”‚ β”‚ β”œβ”€β”€ dto/
β”‚ β”‚ β”‚ β”œβ”€β”€ create-audit.dto.ts
β”‚ β”‚ β”‚ β”œβ”€β”€ update-audit.dto.ts
β”‚ β”‚ β”‚ └── audit-response.dto.ts
β”‚ β”‚ β”œβ”€β”€ entities/
β”‚ β”‚ β”‚ └── audit.entity.ts
β”‚ β”‚ └── audit.controller.spec.ts
β”‚ β”œβ”€β”€ common/ # Shared utilities
β”‚ β”‚ β”œβ”€β”€ decorators/
β”‚ β”‚ β”‚ β”œβ”€β”€ roles.decorator.ts
β”‚ β”‚ β”‚ └── current-user.decorator.ts
β”‚ β”‚ β”œβ”€β”€ guards/
β”‚ β”‚ β”‚ β”œβ”€β”€ jwt-auth.guard.ts
β”‚ β”‚ β”‚ └── roles.guard.ts
β”‚ β”‚ β”œβ”€β”€ interceptors/
β”‚ β”‚ β”‚ β”œβ”€β”€ transform.interceptor.ts
β”‚ β”‚ β”‚ └── logging.interceptor.ts
β”‚ β”‚ β”œβ”€β”€ filters/
β”‚ β”‚ β”‚ └── http-exception.filter.ts
β”‚ β”‚ β”œβ”€β”€ pipes/
β”‚ β”‚ β”‚ └── validation.pipe.ts
β”‚ β”‚ └── utils/
β”‚ β”‚ β”œβ”€β”€ email.util.ts
β”‚ β”‚ β”œβ”€β”€ file-upload.util.ts
β”‚ β”‚ └── validators.util.ts
β”‚ β”œβ”€β”€ database/ # Database configuration
β”‚ β”‚ β”œβ”€β”€ database.module.ts
β”‚ β”‚ β”œβ”€β”€ entities/
β”‚ β”‚ β”‚ β”œβ”€β”€ user.entity.ts
β”‚ β”‚ β”‚ β”œβ”€β”€ project.entity.ts
β”‚ β”‚ β”‚ β”œβ”€β”€ soq.entity.ts
β”‚ β”‚ β”‚ β”œβ”€β”€ tender.entity.ts
β”‚ β”‚ β”‚ β”œβ”€β”€ escrow.entity.ts
β”‚ β”‚ β”‚ └── audit.entity.ts
β”‚ β”‚ └── migrations/
β”‚ β”‚ β”œβ”€β”€ 001-create-users.ts
β”‚ β”‚ β”œβ”€β”€ 002-create-projects.ts
β”‚ β”‚ β”œβ”€β”€ 003-create-soq.ts
β”‚ β”‚ β”œβ”€β”€ 004-create-tenders.ts
β”‚ β”‚ β”œβ”€β”€ 005-create-escrow.ts
β”‚ β”‚ └── 006-create-audit.ts
β”‚ └── auth/ # Authentication module
β”‚ β”œβ”€β”€ auth.module.ts
β”‚ β”œβ”€β”€ auth.controller.ts
β”‚ β”œβ”€β”€ auth.service.ts
β”‚ β”œβ”€β”€ jwt.strategy.ts
β”‚ β”œβ”€β”€ local.strategy.ts
β”‚ └── dto/
β”‚ β”œβ”€β”€ login.dto.ts
β”‚ └── register.dto.ts
β”œβ”€β”€ test/ # E2E tests
β”‚ β”œβ”€β”€ app.e2e-spec.ts
β”‚ β”œβ”€β”€ users.e2e-spec.ts
β”‚ β”œβ”€β”€ projects.e2e-spec.ts
β”‚ β”œβ”€β”€ soq.e2e-spec.ts
β”‚ β”œβ”€β”€ tenders.e2e-spec.ts
β”‚ β”œβ”€β”€ escrow.e2e-spec.ts
β”‚ └── audit.e2e-spec.ts
β”œβ”€β”€ dist/ # Compiled output
β”œβ”€β”€ node_modules/
β”œβ”€β”€ package.json
β”œβ”€β”€ package-lock.json
β”œβ”€β”€ tsconfig.json
β”œβ”€β”€ tsconfig.build.json
β”œβ”€β”€ nest-cli.json
β”œβ”€β”€ .env.example
β”œβ”€β”€ .gitignore
β”œβ”€β”€ Dockerfile
β”œβ”€β”€ docker-compose.yml
└── README.md

οΏ½οΏ½ Key Differences: FastAPI vs NestJS

AspectFastAPINestJS
LanguagePythonTypeScript/JavaScript
ArchitectureFunction-basedClass-based, decorators
StructureFlat modulesFeature modules
ValidationPydantic modelsDTOs with class-validator
DatabaseSQLAlchemyTypeORM/Prisma
TestingpytestJest
Dependency InjectionManualBuilt-in DI container
Guards/InterceptorsDependenciesBuilt-in decorators

πŸ“ Example: User Module in NestJS

src/modules/users/users.controller.ts

import { Controller, Get, Post, Body, Param, UseGuards } from '@nestjs/common';
import { UsersService } from './users.service';
import { CreateUserDto } from './dto/create-user.dto';
import { JwtAuthGuard } from '../../common/guards/jwt-auth.guard';
import { RolesGuard } from '../../common/guards/roles.guard';
import { Roles } from '../../common/decorators/roles.decorator';
@Controller('users')
@UseGuards(JwtAuthGuard, RolesGuard)
export class UsersController {
constructor(private readonly usersService: UsersService) {}
@Post()
@Roles('admin')
create(@Body() createUserDto: CreateUserDto) {
return this.usersService.create(createUserDto);
}
@Get()
@Roles('admin', 'manager')
findAll() {
return this.usersService.findAll();
}
@Get(':id')
@Roles('admin', 'manager')
findOne(@Param('id') id: string) {
return this.usersService.findOne(id);
}
}

src/modules/users/dto/create-user.dto.ts

import { IsEmail, IsString, IsEnum, IsOptional } from 'class-validator';
export class CreateUserDto {
@IsString()
name: string;
@IsEmail()
email: string;
@IsString()
phone: string;
@IsEnum(['Project Owner', 'In-House User', 'Bidder'])
role: string;
@IsOptional()
@IsString()
entity?: string;
@IsOptional()
@IsString()
category?: string;
}

src/modules/users/entities/user.entity.ts

import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn } from 'typeorm';
@Entity('users')
export class User {
@PrimaryGeneratedColumn('uuid')
id: string;
@Column()
name: string;
@Column({ unique: true })
email: string;
@Column()
phone: string;
@Column({
type: 'enum',
enum: ['Project Owner', 'In-House User', 'Bidder'],
default: 'Bidder'
})
role: string;
@Column({ nullable: true })
entity: string;
@Column({ nullable: true })
category: string;
@CreateDateColumn()
createdAt: Date;
@UpdateDateColumn()
updatedAt: Date;
}

πŸš€ NestJS Benefits for Your Project

  1. TypeScript First: Better type safety and IDE support
  2. Modular Architecture: Clean separation of concerns
  3. Built-in DI: Automatic dependency injection
  4. Decorators: Clean, readable API definitions
  5. Guards & Interceptors: Built-in middleware system
  6. Validation: Class-validator integration
  7. Testing: Jest integration out of the box

Would you like me to show you how to set up the NestJS equivalent of your current FastAPI structure?


1. File and Path Check

  • File: src/content/docs/instruction/nestjs+nextjs.md
  • Sidebar Slug: instruction/nestjs+nextjs
  • Expected Path:
    • The slug is always relative to the docs root (src/content/docs/)
    • So, instruction/nestjs+nextjs β†’ src/content/docs/instruction/nestjs+nextjs.md

You have this correct.


2. Case Sensitivity & Typos

  • Make sure the filename is exactly nestjs+nextjs.md (all lowercase, no spaces, no hidden characters).
  • Make sure the folder is exactly instruction (all lowercase).

3. Sidebar Config

  • Your config should be:
    { label: 'Full-Stack Nest + Next.js Example', slug: 'instruction/nestjs+nextjs' }
  • No .md in the slug.

4. Astro/Content Caching

  • Restart your Astro dev server after any file or config changes.
  • If you use pnpm, yarn, or npm, try:
    rm -rf .astro
    rm -rf node_modules/.vite
    npm run dev
    (or the equivalent for your package manager)

5. Check for Duplicate or Conflicting Files

  • Make sure there is only one file named nestjs+nextjs.md in your entire src/content/docs/ tree.
  • No uppercase variants, no .mdx with the same name.

6. Astro Version / Starlight Version

  • If you recently upgraded Astro or Starlight, sometimes a full clean and reinstall helps:
    rm -rf node_modules
    rm package-lock.json # or yarn.lock or pnpm-lock.yaml
    npm install
    npm run dev

7. Check for Hidden Characters

  • Sometimes, especially if the file was renamed in Finder or via copy-paste, there can be hidden characters.
  • Run this in your project root to check:
    ls -l src/content/docs/instruction/
    Make sure the filename is exactly nestjs+nextjs.md.

8. Try Renaming the File

  • Rename nestjs+nextjs.md to nestjs-nextjs.md
  • Update the sidebar slug to instruction/nestjs-nextjs
  • Restart the dev server

Sometimes the + character can cause issues in some static site generators or their routing logic.


9. Check for .mdx Extension

  • If you accidentally created nestjs+nextjs.mdx, the slug would be different. Make sure the extension is .md.

10. Final: Minimal Sidebar Test

Try a minimal sidebar config with only this entry to isolate the problem:

sidebar: [
{
label: 'Instruction',
items: [
{ label: 'Full-Stack Nest + Next.js Example', slug: 'instruction/nestjs+nextjs' }
]
}
]

If this still fails, the issue is with file recognition, not config.


Summary of What to Try Next

  1. Rename nestjs+nextjs.md β†’ nestjs-nextjs.md
  2. Update sidebar slug to instruction/nestjs-nextjs
  3. Restart Astro dev server
  4. If it works, the + was the problem.

Would you like me to automatically rename the file and update your config for you? This is the most likely fix!