JSPM

  • Created
  • Published
  • Downloads 84
  • Score
    100M100P100Q101114F
  • License ISC

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 integration for scalable full-stack applications.

Package Exports

  • @nitronjs/framework

Readme

NitronJS

A modern full-stack Node.js framework with React Server Components, Islands Architecture, and built-in tooling. Built on React and Fastify for high performance and developer productivity.

Why NitronJS?

  • Zero Config - Start building immediately. No webpack, no babel config, no boilerplate
  • Server Components by Default - All components render on the server. Add "use client" only where needed
  • Islands Architecture - Client components hydrate independently, keeping your pages fast
  • Instant HMR - Changes reflect immediately without losing state
  • Full-Stack - Database ORM, authentication, sessions, validation all built-in

Installation

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

Server Components (Default)

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.

// 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 (
        <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.

// resources/views/Components/Counter.tsx
"use client";
import { useState } from 'react';

export default function Counter() {
    const [count, setCount] = useState(0);

    return (
        <button onClick={() => setCount(count + 1)}>
            Count: {count}
        </button>
    );
}

Use client components inside server components:

// resources/views/Site/Home.tsx (Server Component)
import Counter from '../Components/Counter';

export default function Home() {
    return (
        <div>
            <h1>Welcome</h1>
            <Counter /> {/* Hydrates as an island */}
        </div>
    );
}

Layouts

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
// resources/views/Layout.tsx
export default function RootLayout({ children }) {
    return (
        <html lang="en">
            <head>
                <meta charSet="UTF-8" />
                <title>My App</title>
            </head>
            <body>
                {children}
            </body>
        </html>
    );
}
// resources/views/Admin/Layout.tsx
export default function AdminLayout({ children }) {
    return (
        <div className="admin-wrapper">
            <nav>Admin Navigation</nav>
            <main>{children}</main>
        </div>
    );
}

Disable layout for a specific page:

export const layout = false;

export default function FullscreenPage() {
    return <div>No layout wrapper</div>;
}

Routing

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');

// 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:

<a href={route('user.show', { id: 1 })}>View User</a>

Controllers

// 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

Models

// app/Models/User.js
import { Model } from '@nitronjs/framework';

export default class User extends Model {
    static table = 'users';
}

Queries

// 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();

Migrations

npm run make:migration create_users_table
// 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:

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

// 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

// 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

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:

// 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:

export default {
    routeMiddlewares: {
        'check-age': CheckAge,
        'auth': AuthMiddleware,
    }
};

Apply to routes:

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.

/* resources/css/app.css */
@import "tailwindcss";

CSS files are automatically linked in your views:

// CSS from resources/css/ is available at /storage/css/
<link rel="stylesheet" href="/storage/css/app.css" />

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 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 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:

import { Config } from '@nitronjs/framework';

const appName = Config.get('app.name');
const dbHost = Config.get('database.host', 'localhost'); // with default

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+

Documentation

Full documentation available at nitronjs.dev

License

ISC