Getting started

Installation

Here's how to add nestjs-mvc to a NestJS project. You'll need NestJS 12, React 19 or Vue 3.5, and Node 20.19 or newer.

Every page in these docs shows its examples in React or in Vue. Pick yours, and the whole site follows:

Install the packages

Run this in your NestJS project:

npm install nestjs-mvc @inertiajs/react react react-dom
npm install -D vite @vitejs/plugin-react @types/react @types/react-dom

@inertiajs/react is what runs nestjs-mvc in the browser. It needs to be installed, but you'll import everything from nestjs-mvc/react, so you won't use it directly.

npm install nestjs-mvc @inertiajs/vue3 vue
npm install -D vite @vitejs/plugin-vue vue-tsc

@inertiajs/vue3 is what runs nestjs-mvc in the browser. It needs to be installed, but you'll import everything from nestjs-mvc/vue, so you won't use it directly.

Configure Vite

Vite builds your pages. Create a vite.config.ts in the root of your project:

// vite.config.ts
import react from '@vitejs/plugin-react'
import { nestjsMvc } from 'nestjs-mvc/vite'
import { defineConfig } from 'vite'

export default defineConfig({
  plugins: [react(), nestjsMvc()],
})
// vite.config.ts
import vue from '@vitejs/plugin-vue'
import { nestjsMvc } from 'nestjs-mvc/vite'
import { defineConfig } from 'vite'

export default defineConfig({
  plugins: [vue(), nestjsMvc()],
})

You don't need an entry file. The plugin sees in your package.json whether you use React or Vue, looks for your pages in frontend/pages, and if there's a frontend/app.css it loads that too.

Register the module

Import MvcModule in your root module:

// src/app.module.ts
import { Module } from '@nestjs/common'
import { MvcModule } from 'nestjs-mvc'

@Module({
  imports: [MvcModule.forRoot({ vite: {} })],
})
export class AppModule {}

With vite: {}, Vite runs inside your Nest app while you're developing. In production it serves the files you built instead.

Validate requests

Add a validation pipe in main.ts. The exception factory is what sends validation errors back to your forms, which you'll see on the forms page.

// src/main.ts
import { ValidationPipe } from '@nestjs/common'
import { NestFactory } from '@nestjs/core'
import { validationExceptionFactory } from 'nestjs-mvc'
import { AppModule } from './app.module'

async function bootstrap() {
  const app = await NestFactory.create(AppModule)
  app.useGlobalPipes(new ValidationPipe({ exceptionFactory: validationExceptionFactory }))
  await app.listen(3000)
}
bootstrap()

This uses class-validator, so install it if you haven't already:

npm install class-validator class-transformer

TypeScript

Your pages should stay out of your server build:

// tsconfig.build.json
{
  "extends": "./tsconfig.json",
  "exclude": ["node_modules", "test", "dist", "**/*spec.ts", "frontend"]
}

Your pages are .tsx files, so TypeScript needs to know about JSX:

// tsconfig.json
{
  "compilerOptions": {
    "jsx": "react-jsx"
  }
}

Your pages are .vue files, which tsc can't read. vue-tsc can, with a small config of its own:

// tsconfig.vue.json
{
  "extends": "./tsconfig.json",
  "compilerOptions": { "jsx": "preserve" },
  "include": ["frontend/**/*.vue", "frontend/**/*.ts"]
}
npx vue-tsc -p tsconfig.vue.json --noEmit

Start the app

npm run start:dev

There's nothing to look at yet. You'll build your first page next.

One process

Vite runs inside your Nest app on the same port, so you won't need a second terminal for it.

Previous
Introduction