NitronJS is a modern and extensible Node.js MVC framework built on Fastify. It focuses on clean architecture, modular structure, and developer productivity, offering built-in routing, middleware, configuration management, CLI tooling, and native React int
npm install @nitronjs/framework"use client" only where needed
bash
npx -y @nitronjs/framework my-app
cd my-app
npm run dev
`
> Note: The -y flag skips npm's confirmation prompt for a smoother experience.
Your app will be running at http://localhost:3000
Core Concepts
$3
Every .tsx file in resources/views/ is a Server Component by default. They run on the server, have full access to your database, file system, and never ship JavaScript to the browser.
`tsx
// resources/views/Site/Home.tsx
import User from '@/app/Models/User.js';
export default async function Home() {
const users = await User.get(); // Direct database access
return (
Users
{users.map(user => (
- {user.name}
))}
);
}
`
$3
Add "use client" at the top of a file to make it interactive. These components hydrate on the browser and can use React hooks.
`tsx
// resources/views/Components/Counter.tsx
"use client";
import { useState } from 'react';
export default function Counter() {
const [count, setCount] = useState(0);
return (
);
}
`
Use client components inside server components:
`tsx
// resources/views/Site/Home.tsx (Server Component)
import Counter from '../Components/Counter';
export default function Home() {
return (
Welcome
{/ Hydrates as an island /}
);
}
`
$3
Create Layout.tsx files to wrap pages. Layouts are discovered automatically by walking up the directory tree.
`
resources/views/
├── Layout.tsx # Root layout (wraps everything)
├── Site/
│ └── Home.tsx # Uses root Layout
└── Admin/
├── Layout.tsx # Admin layout (nested inside root)
└── Dashboard.tsx # Uses both layouts
`
`tsx
// resources/views/Layout.tsx
export default function RootLayout({ children }) {
return (
My App
{children}
);
}
`
`tsx
// resources/views/Admin/Layout.tsx
export default function AdminLayout({ children }) {
return (
{children}
);
}
`
Disable layout for a specific page:
`tsx
export const layout = false;
export default function FullscreenPage() {
return No layout wrapper;
}
`
Routing
Define routes in routes/web.js:
`javascript
import { Route } from '@nitronjs/framework';
import HomeController from '#app/Controllers/HomeController.js';
import UserController from '#app/Controllers/UserController.js';
// Basic routes
Route.get('/', HomeController.index).name('home');
Route.get('/about', HomeController.about).name('about');
// Route parameters
Route.get('/users/:id', UserController.show).name('user.show');
// Route groups with middleware
Route.prefix('/admin').middleware('auth').group(() => {
Route.get('/dashboard', AdminController.dashboard).name('admin.dashboard');
Route.get('/users', AdminController.users).name('admin.users');
});
// RESTful routes
Route.post('/users', UserController.store);
Route.put('/users/:id', UserController.update);
Route.delete('/users/:id', UserController.destroy);
`
Generate URLs using route names:
`tsx
View User
`
Controllers
`javascript
// app/Controllers/UserController.js
class UserController {
static async index(req, res) {
const users = await User.get();
return res.view('User/Index', { users });
}
static async show(req, res) {
const user = await User.find(req.params.id);
if (!user) {
return res.status(404).view('Errors/NotFound');
}
return res.view('User/Show', { user });
}
static async store(req, res) {
const { name, email } = req.body;
const user = new User();
user.name = name;
user.email = email;
await user.save();
return res.redirect(route('user.show', { id: user.id }));
}
}
export default UserController;
`
Database
$3
`javascript
// app/Models/User.js
import { Model } from '@nitronjs/framework';
export default class User extends Model {
static table = 'users';
}
`
$3
`javascript
// Find all
const users = await User.get();
// Find by ID
const user = await User.find(1);
// Where clauses
const admins = await User.where('role', '=', 'admin').get();
const active = await User.where('active', true).get();
// Chaining
const results = await User
.where('role', '=', 'admin')
.where('active', true)
.orderBy('created_at', 'desc')
.limit(10)
.get();
// First result
const user = await User.where('email', '=', 'john@example.com').first();
// Create
const user = new User();
user.name = 'John';
user.email = 'john@example.com';
await user.save();
// Update
await User.where('id', '=', 1).update({ name: 'Jane' });
// Delete
await User.where('id', '=', 1).delete();
`
$3
`bash
npm run make:migration create_users_table
`
`javascript
// database/migrations/2024_01_01_000000_create_users_table.js
import { Schema } from '@nitronjs/framework';
class CreateUsersTable {
static async up() {
await Schema.create('users', (table) => {
table.id();
table.string('name');
table.string('email').unique();
table.string('password');
table.timestamp('created_at');
table.timestamp('updated_at').nullable();
});
}
static async down() {
await Schema.dropIfExists('users');
}
}
export default CreateUsersTable;
`
Run migrations:
`bash
npm run migrate # Run pending migrations
npm run migrate:fresh # Drop all tables and re-run
npm run migrate:fresh --seed # Drop, migrate, and seed
`
Authentication
`javascript
// In a controller or middleware
class AuthController {
static async login(req, res) {
const { email, password } = req.body;
const success = await req.auth.attempt({ email, password });
if (success) {
return req.redirect('/dashboard');
}
return req.view('Auth/Login', {
error: 'Invalid credentials'
});
}
static async logout(req, res) {
await req.auth.logout();
return res.redirect('/');
}
}
// Check authentication
if (req.auth.check()) {
const user = await req.auth.user();
}
`
Session
`javascript
// Set value
req.session.set('key', 'value');
// Get value
const value = req.session.get('key');
// Flash messages (one-time)
req.session.flash('success', 'Profile updated!');
const message = req.session.getFlash('success');
// All data
const data = req.session.all();
// Regenerate session ID (after login)
req.session.regenerate();
// CSRF token
const token = req.session.getCsrfToken();
`
Validation
`javascript
import { Validator } from '@nitronjs/framework';
const validation = Validator.make(req.body, {
name: 'required|string|min:2|max:100',
email: 'required|email',
password: 'required|string|min:8',
age: 'numeric|min:18'
});
if (validation.fails()) {
return res.status(422).send({
errors: validation.errors()
});
}
`
Middleware
Create custom middleware:
`javascript
// app/Middlewares/CheckAge.js
class CheckAge {
static async handler(req, res) {
if (req.query.age < 18) {
return res.status(403).send('Access denied');
}
// No return = continue to next handler
}
}
export default CheckAge;
`
Register in app/Kernel.js:
`javascript
export default {
routeMiddlewares: {
'check-age': CheckAge,
'auth': AuthMiddleware,
}
};
`
Apply to routes:
`javascript
Route.middleware('check-age').get('/restricted', Controller.method);
Route.middleware('auth', 'check-age').get('/admin', AdminController.index);
`
CSS & Tailwind
Put your CSS files in resources/css/. Tailwind CSS is automatically detected and processed.
`css
/ resources/css/app.css /
@import "tailwindcss";
`
CSS files are automatically linked in your views:
`tsx
// CSS from resources/css/ is available at /storage/css/
`
CLI Commands
`bash
Development
npm run dev # Start dev server with HMR
npm run build # Build for production
npm run start # Start production server
Database
npm run migrate # Run migrations
npm run migrate:fresh # Fresh migration
npm run seed # Run seeders
Code Generation
npm run make:controller
npm run make:model
npm run make:middleware
npm run make:migration
npm run make:seeder
Utilities
npm run storage:link # Create storage symlink
`
Project Structure
`
my-app/
├── app/
│ ├── Controllers/ # Request handlers
│ ├── Middlewares/ # Custom middleware
│ └── Models/ # Database models
├── config/ # Configuration files
│ ├── app.js
│ ├── database.js
│ └── server.js
├── database/
│ ├── migrations/ # Database migrations
│ └── seeders/ # Database seeders
├── public/ # Static assets
├── resources/
│ ├── css/ # Stylesheets
│ └── views/ # React components (TSX)
├── routes/
│ └── web.js # Route definitions
├── storage/ # File storage
└── .env # Environment variables
`
Configuration
Access configuration values:
`javascript
import { Config } from '@nitronjs/framework';
const appName = Config.get('app.name');
const dbHost = Config.get('database.host', 'localhost'); // with default
`
Environment variables in .env:
`env
APP_NAME=MyApp
APP_DEV=true
APP_PORT=3000
DATABASE_HOST=127.0.0.1
DATABASE_PORT=3306
DATABASE_NAME=myapp
DATABASE_USERNAME=root
DATABASE_PASSWORD=
``