@nitronjs/framework
v0.3.10
Published
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
Downloads
1,195
Readme
Quick Start
npx -y @nitronjs/framework my-app
cd my-app
npm run storage:link
npm run devYour app will be running at http://localhost:3000
Core Concepts
Server Components (Default)
Every .tsx file in resources/views/ is a Server Component by default. They run on the server and have full access to your database and file system.
// resources/views/Site/Home.tsx
import User from '@models/User';
export default async function Home() {
const users = await User.get();
return (
<div>
<h1>Users</h1>
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
</div>
);
}Client Components
Add "use client" at the top of a file to make it interactive. These components hydrate on the browser and can use React hooks.
"use client";
import { useState } from 'react';
export default function Counter() {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount(count + 1)}>
Count: {count}
</button>
);
}Layouts
Create Layout.tsx files to wrap pages. Layouts are discovered automatically by walking up the directory tree. During SPA navigation, layouts persist and only page content is updated — no full page reloads.
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 layoutsRouting
Define routes in routes/web.js:
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');
// RESTful routes
Route.post('/users', UserController.store).name('user.store');
Route.put('/users/:id', UserController.update).name('user.update');
Route.delete('/users/:id', UserController.destroy).name('user.destroy');
// Route groups with prefix, name prefix, and middleware
Route.prefix('/admin').name('admin.').middleware('auth').group(() => {
Route.get('/', DashboardController.index).name('dashboard');
Route.get('/users', AdminUserController.index).name('users');
// Nested groups
Route.prefix('/pages').name('pages.').group(() => {
Route.get('/:id/edit', PagesController.edit).name('edit');
});
});
// Middleware-only groups
Route.middleware('guest').group(() => {
Route.get('/login', AuthController.getLogin).name('login');
Route.post('/login', AuthController.postLogin);
});URL Generation
The global route() function is available everywhere — controllers, views, and client-side code:
// Basic
route('home') // => "/"
// With parameters
route('user.show', { id: 1 }) // => "/users/1"
// With query string
route('admin.users', {}, { page: 2, q: 'search' }) // => "/admin/users?page=2&q=search"
// Parameters + query string
route('admin.pages.edit', { id: 5 }, { tab: 'seo' }) // => "/admin/pages/5/edit?tab=seo"Controllers
// app/Controllers/UserController.js
import User from '../Models/User.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;Path Aliases
NitronJS provides built-in path aliases for clean imports in .tsx view files:
| Alias | Path |
|---|---|
| @models/* | app/Models/* |
| @controllers/* | app/Controllers/* |
| @middlewares/* | app/Middlewares/* |
| @views/* | resources/views/* |
| @css/* | resources/css/* |
| @/* | Project root |
Features
Database
Models
import { Model } from '@nitronjs/framework';
export default class User extends Model {
static table = 'users';
}Queries
const users = await User.get();
const user = await User.find(1);
const admins = await User.where('role', '=', 'admin').get();
const results = await User
.where('active', true)
.orderBy('created_at', 'desc')
.limit(10)
.get();
const user = await User.where('email', '=', '[email protected]').first();
// Aggregates
const total = await User.count();
const maxAge = await User.max('age');
// Create
const user = new User();
user.name = 'John';
user.email = '[email protected]';
await user.save();
// Update
await User.where('id', '=', 1).update({ name: 'Jane' });
// Delete
await User.where('id', '=', 1).delete();Migrations
npm run make:migration create_posts_tableimport { Schema } from '@nitronjs/framework';
class CreatePostsTable {
static async up() {
await Schema.create('posts', (table) => {
table.id();
table.string('title');
table.text('body');
table.string('slug').unique();
table.boolean('published').default(false);
table.json('metadata').nullable();
table.timestamp('created_at');
table.timestamp('updated_at').nullable();
});
}
static async down() {
await Schema.dropIfExists('posts');
}
}
export default CreatePostsTable;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
npm run migrate:rollback # Rollback last batch
npm run migrate:status # Show migration statusAuthentication
class AuthController {
static async login(req, res) {
const { email, password } = req.body;
const success = await req.auth.attempt({ email, password });
if (success) {
return res.redirect(route('dashboard'));
}
return res.view('Auth/Login', {
error: 'Invalid credentials'
});
}
static async logout(req, res) {
await req.auth.logout();
return res.redirect(route('home'));
}
}
// Check authentication
if (req.auth.check()) {
const user = await req.auth.user();
}
// Named guards
req.auth.guard('admin').check();Sessions
req.session.set('key', 'value');
const value = req.session.get('key');
// Flash messages
req.session.flash('success', 'Profile updated!');
const message = req.session.getFlash('success');
// Regenerate session ID
req.session.regenerate();Validation
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|confirmed',
age: 'numeric|min:18',
avatar: 'file|mimes:png,jpg|max:2097152'
});
if (validation.fails()) {
return res.status(422).send({
errors: validation.errors()
});
}Middleware
// app/Middlewares/CheckAge.js
class CheckAge {
static async handler(req, res) {
if (req.query.age < 18) {
return res.status(403).send('Access denied');
}
}
}
export default CheckAge;Register in app/Kernel.js:
export default {
routeMiddlewares: {
'check-age': CheckAge,
'auth': AuthMiddleware
}
};import { Mail } from '@nitronjs/framework';
await Mail.from('[email protected]')
.to('[email protected]')
.subject('Welcome!')
.html('<h1>Hello</h1>')
.attachment({ filename: 'file.pdf', path: '/path/to/file.pdf' })
.send();
// Using a template
await Mail.to('[email protected]')
.view('emails/welcome', { name: 'Alice' })
.send();Storage
import { Storage } from '@nitronjs/framework';
await Storage.put(file, 'upload_files', 'image.jpg');
const buffer = await Storage.get('upload_files/image.jpg');
await Storage.delete('upload_files/old.jpg');
await Storage.move('upload_files/a.jpg', 'upload_files/b.jpg');
Storage.exists('upload_files/image.jpg');
Storage.url('upload_files/image.jpg'); // => "/storage/upload_files/image.jpg"Encryption
import { AES } from '@nitronjs/framework';
const token = AES.encrypt({ userId: 1, expires: '2025-12-31' });
const data = AES.decrypt(token); // Returns false on tamperHashing
import { Hash } from '@nitronjs/framework';
const hashed = await Hash.make('password123');
const valid = await Hash.check('password123', hashed);Logging
import { Log } from '@nitronjs/framework';
Log.info('User registered', { userId: 1 });
Log.error('Payment failed', { orderId: 123 });
Log.debug('Query executed', { sql: '...' });DateTime
import { DateTime } from '@nitronjs/framework';
DateTime.toSQL(); // "2025-01-15 10:30:00"
DateTime.getDate(timestamp, 'Y-m-d H:i:s');
DateTime.addDays(7);
DateTime.subHours(2);Faker
Built-in fake data generator for seeders and testing:
import { Faker } from '@nitronjs/framework';
Faker.fullName(); // "John Smith"
Faker.email(); // "[email protected]"
Faker.sentence(); // "Lorem ipsum dolor sit amet."
Faker.int(1, 100); // 42
Faker.boolean(); // true
Faker.uuid(); // "550e8400-e29b-41d4-a716-446655440000"
Faker.creditCard(); // Luhn-valid card number
Faker.hexColor(); // "#a3f29c"
Faker.oneOf(['a', 'b', 'c']);String Utilities
import { Str } from '@nitronjs/framework';
Str.slug('Hello World'); // "hello-world"
Str.camel('user_name'); // "userName"
Str.pascal('user_name'); // "UserName"
Str.snake('userName'); // "user_name"
Str.random(32); // Random string
Str.limit('Long text...', 10); // "Long text..."CLI Commands
# 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 migrate:fresh:seed # Fresh migration + seed
npm run migrate:rollback # Rollback last batch
npm run migrate:status # Show migration status
npm run seed # Run seeders
# Code Generation
npm run make:controller <name>
npm run make:model <name>
npm run make:middleware <name>
npm run make:migration <name>
npm run make:seeder <name>
# Utilities
npm run storage:link # Create storage symlinkProject Structure
my-app/
├── app/
│ ├── Controllers/ # Request handlers
│ ├── Middlewares/ # Custom middleware
│ └── Models/ # Database models
├── config/ # Configuration files
│ ├── app.js
│ ├── auth.js
│ ├── database.js
│ ├── hash.js
│ ├── server.js
│ └── session.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 variablesCSS & Tailwind
Put your CSS files in resources/css/. Tailwind CSS v4 is automatically detected and processed.
/* resources/css/global.css */
@import "tailwindcss";Import in your .tsx files using the @css alias:
import "@css/global.css";Configuration
Access configuration values:
import { Config } from '@nitronjs/framework';
const appName = Config.get('app.name');
const dbHost = Config.get('database.host', 'localhost');Environment variables in .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=Requirements
- Node.js 18+
- MySQL 5.7+ or MariaDB 10.3+
License
ISC
