npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

laranode-cli

v1.0.20

Published

The LaraNode Framework CLI Installer

Downloads

1,144

Readme


About LaraNode

LaraNode is a powerful, expressive, and elegant Node.js framework designed to mirror the architecture and developer experience of Laravel. Built on top of Express.js, it provides a robust foundation for building modern web applications with a familiar, elegant API.

If you know Laravel, you already know LaraNode.

Installation

Using LaraNode CLI (Recommended)

# Install the CLI globally
npm install -g laranode-cli

# Create a new project
laranode new my-app

# Start the development server
cd my-app
node artisan serve

Manual Installation

# Clone the repository
git clone https://github.com/Sabbir1993/laranode.git my-app
cd my-app

# Install dependencies
npm install

# Configure environment
cp .env.example .env
# Edit .env and set your APP_KEY

# Start the server
node server.js

Features

| Feature | Description | |---|---| | 🏛️ Laravel Architecture | Familiar directory structure — app/, routes/, config/, resources/, database/ | | 🧩 Service Container | Powerful IoC container with dependency injection and service providers | | 🛣️ Expressive Routing | Route groups, middleware, prefixes, named routes, and resource controllers | | 🗄️ Loquent ORM | Active Record ORM with relationships, eager loading, soft deletes, and query builder | | ✨ Edge Templates | Blade-like templating engine with layouts, partials, and directives | | 🛡️ Authentication | Session-based auth & Sanctum-style API tokens out of the box | | 🔒 Security Middleware | CSRF protection, CORS, rate limiting, security headers, and input sanitization | | ✅ Validation | Form request validation with 20+ built-in rules and custom rule support | | 🔐 Encryption & Hashing | AES-256 encryption and bcrypt hashing via Facades | | 📟 Artisan CLI | Command-line interface for migrations, scaffolding, and dev server | | 📊 Log Viewer | Built-in web-based log viewer with severity filtering and pagination | | 🌐 Translations | Multi-language support with language files and the Lang facade | | 🗃️ Multi-Database | SQLite and MySQL support with connection pooling | | 📄 Pagination | Built-in query pagination with page metadata and HTML links | | 📮 Queues | Database-driven job queue with dispatch, delay, retries, and failed jobs | | 🎧 Events & Listeners | App-wide event dispatching with listener DI resolution | | 🚀 Cache | Unified cache API with file and memory drivers (Cache.remember) | | 📁 Storage | Unified filesystem API (Storage.put) with visibility, mime-type, and symbolic links | | 📩 Mail | Fluent email sending API via Mailables with automatic queuing support | | 🔔 Notifications | Multi-channel notifications routing (Mail, Database, Broadcast) with Notifiable trait | | 📡 Broadcasting | Real-time event broadcasting via Socket.io with ShouldBroadcast trait | | 🧪 Testing | Robust testing framework with artisan test and HTTP assertions | | ⚡ Performance | Route and Config caching for production optimization (node artisan route:cache) |


Quick Start

Directory Structure

my-app/
├── app/
│   ├── Console/          # Artisan commands
│   ├── Exceptions/       # Exception handlers
│   ├── Http/
│   │   ├── Controllers/  # Route controllers
│   │   ├── Kernel.js     # HTTP middleware stack
│   │   ├── Middleware/    # Custom middleware
│   │   └── Requests/     # Form request validation
│   ├── Models/           # Loquent models
│   └── Rules/            # Custom validation rules
├── bootstrap/
│   └── app.js            # Application bootstrapper
├── config/
│   ├── app.js            # App name, env, providers
│   ├── auth.js           # Guards, providers, passwords
│   ├── database.js       # Database connections
│   └── logging.js        # Log channels & log viewer
├── database/
│   └── migrations/       # Database migrations
├── public/               # Static assets
├── resources/
│   └── views/            # Edge templates
├── routes/
│   ├── web.js            # Web routes (session, CSRF)
│   └── api.js            # API routes (stateless)
├── storage/
│   ├── app/              # Application files
│   ├── framework/views/  # Compiled views
│   └── logs/             # Log files
├── vendor/               # Framework source
├── .env                  # Environment variables
├── artisan.js            # CLI entry point
└── server.js             # HTTP server entry point

Documentation

Routing

Define routes in routes/web.js (with session & CSRF) or routes/api.js (stateless):

const Route = use('laranode/Support/Facades/Route');

// Basic routes
Route.get('/', (req, res) => res.view('welcome'));
Route.post('/submit', 'FormController@store');

// Named routes
Route.get('/login', 'AuthController@showLoginForm').name('login');

// Route groups with middleware and prefix
Route.group({ middleware: ['auth'], prefix: '/admin' }, () => {
    Route.get('/dashboard', 'AdminController@index');
    Route.get('/users', 'AdminController@users');
});

// Route parameters
Route.get('/users/{id}', 'UserController@show');
Route.put('/users/{user}', 'UserController@update');
Route.delete('/users/{user}', 'UserController@destroy');

Controllers

Controllers reside in app/Http/Controllers/:

const Controller = use('laranode/Routing/Controller/Controller');

class HomeController extends Controller {

    async index(req, res) {
        const users = await User.all();
        return res.view('home', { users });
    }

    async store(req, res) {
        const data = req.only(['name', 'email']);
        const user = await User.create(data);
        return res.redirect('/users');
    }

    async show(req, res) {
        const user = await User.find(req.params.id);
        return res.json({ user });
    }
}

module.exports = HomeController;

Loquent ORM

Loquent is LaraNode's Active Record ORM, inspired by Laravel's Eloquent:

const Model = use('laranode/Database/Loquent/Model');

class User extends Model {
    static table = 'users';
    static fillable = ['name', 'email', 'password'];
    static hidden = ['password', 'remember_token'];
    static casts = { is_admin: 'boolean' };

    todos() {
        const Todo = use('App/Models/Todo');
        return this.hasMany(Todo, 'user_id');
    }
}

module.exports = User;

CRUD Operations

// Create
const user = await User.create({ name: 'John', email: '[email protected]' });

// Read
const all     = await User.all();
const first   = await User.first();
const found   = await User.find(1);
const many    = await User.findMany([1, 2, 3]);
const fail    = await User.findOrFail(1);     // throws ModelNotFoundException
const fail2   = await User.firstOrFail();     // throws ModelNotFoundException

// Find or Create
const user = await User.firstOrCreate({ email: '[email protected]' }, { name: 'John' });
const user = await User.firstOrNew({ email: '[email protected]' }, { name: 'John' });
const user = await User.updateOrCreate({ email: '[email protected]' }, { name: 'Jane' });

// Update
await User.where('id', 1).update({ name: 'Jane' });

// Instance update & save
const user = await User.find(1);
user.name = 'Jane';
await user.save();

// Delete
await User.where('id', 1).delete();
await User.destroy([1, 2, 3]);         // Delete by IDs

Query Builder — Where Clauses

// Basic where
User.where('active', true)
User.where('age', '>', 18)
User.orWhere('role', 'admin')

// Null checks
User.whereNull('deleted_at')
User.whereNotNull('email_verified_at')

// In / Not In
User.whereIn('status', ['active', 'pending'])
User.whereNotIn('role', ['banned'])

// Between
User.whereBetween('age', [18, 65])
User.whereNotBetween('price', [100, 500])

// Date where clauses
User.whereDate('created_at', '2026-01-01')
User.whereMonth('created_at', 3)
User.whereYear('created_at', 2026)
User.whereDay('created_at', 15)

// Column comparison
User.whereColumn('updated_at', '>', 'created_at')

// Raw where
User.whereRaw('age > ? AND status = ?', [18, 'active'])

// Exists
User.whereExists(query => query.select('*').from('orders').whereRaw('orders.user_id = users.id'))

Query Builder — Selecting & Ordering

// Select specific columns
User.select('id', 'name', 'email').get()
User.selectRaw('COUNT(*) as total, status').groupBy('status').get()
User.distinct().select('role').get()

// Ordering
User.orderBy('created_at', 'desc').get()
User.orderByRaw('FIELD(status, "active", "pending", "inactive")').get()
User.latest().get()                    // ORDER BY created_at DESC
User.oldest().get()                    // ORDER BY created_at ASC
User.inRandomOrder().get()

// Limit & Offset
User.limit(10).offset(20).get()

// Conditional (when)
User.when(req.query.role, (query, role) => query.where('role', role)).get()

Query Builder — Joins

User.join('orders', 'users.id', '=', 'orders.user_id').get()
User.leftJoin('profiles', 'users.id', '=', 'profiles.user_id').get()
User.rightJoin('orders', 'users.id', '=', 'orders.user_id').get()
User.crossJoin('roles').get()

// Closure-based joins (multiple conditions)
User.join('posts', (join) => {
    join.on('users.id', '=', 'posts.user_id')
        .on('users.tenant_id', '=', 'posts.tenant_id');
}).get()

// Subquery Joins (joinSub / leftJoinSub / rightJoinSub)
const latestPosts = DB.table('posts')
    .select('user_id')
    .selectRaw('MAX(created_at) as last_post')
    .groupBy('user_id');

DB.table('users')
    .joinSub(latestPosts, 'latest_posts', 'users.id', '=', 'latest_posts.user_id')
    .select('users.*', 'latest_posts.last_post')
    .get()

// Subquery join with multiple conditions
DB.table('users')
    .joinSub(latestPosts, 'lp', (join) => {
        join.on('users.id', '=', 'lp.user_id')
            .on('users.tenant_id', '=', 'lp.tenant_id');
    })
    .get()

// leftJoinSub
DB.table('users')
    .leftJoinSub(orderTotals, 'summary', 'users.id', '=', 'summary.user_id')
    .get()

Query Builder — Grouping & Aggregates

// Grouping
User.groupBy('role').selectRaw('role, COUNT(*) as total').get()
User.groupBy('role').having('total', '>', 5).get()

// Aggregates
const count = await User.count();
const max   = await User.max('age');
const min   = await User.min('age');
const avg   = await User.avg('salary');
const sum   = await User.sum('balance');
const has   = await User.where('role', 'admin').exists();
const none  = await User.where('role', 'banned').doesntExist();

Query Builder — Insert Variants

const DB = use('laranode/Support/Facades/DB');

// Basic insert
await DB.table('users').insert({ name: 'John', email: '[email protected]' });

// Insert and get ID
const id = await DB.table('users').insertGetId({ name: 'Jane' });

// Insert or ignore (skip duplicates)
await DB.table('users').insertOrIgnore({ email: '[email protected]' });

// Upsert (insert or update on duplicate key)
await DB.table('users').upsert(
    [{ email: '[email protected]', name: 'John Updated' }],
    ['email'],  // unique columns
    ['name']    // columns to update on conflict
);

Query Builder — Update & Delete

// Increment / Decrement
await User.where('id', 1).increment('login_count');
await User.where('id', 1).increment('balance', 100, { last_deposit: new Date() });
await User.where('id', 1).decrement('balance', 50);

// Truncate table
await DB.table('logs').truncate();

#### Database Transactions

LaraNode's DB facade provides a simple way to run a set of operations within a database transaction. If an exception is thrown within the closure, the transaction is automatically rolled back.

```javascript
await DB.transaction(async () => {
    await DB.table('users').update({ votes: 1 });
    await DB.table('posts').delete();

    // Loquent models automatically use the active transaction
    await User.create({ name: 'Transaction User' });
});

#### Chunking & Pagination

```javascript
// Process large datasets in chunks
await User.where('active', true).chunk(100, async (users, page) => {
    for (const user of users) {
        // process each user
    }
    // return false to stop chunking
});

// Pluck values
const emails = await User.pluck('email');
const emailMap = await User.pluck('email', 'id');    // { 1: '[email protected]', 2: '[email protected]' }

// Get a single column value
const name = await User.where('id', 1).value('name');

Relationships

class User extends Model {
    // One-to-One
    phone()   { return this.hasOne(Phone, 'user_id'); }

    // One-to-Many
    posts()   { return this.hasMany(Post, 'user_id'); }

    // Belongs To (inverse)
    team()    { return this.belongsTo(Team, 'team_id'); }

    // Many-to-Many (with pivot)
    roles()   {
        return this.belongsToMany(Role, 'role_user', 'user_id', 'role_id')
                   .withPivot('assigned_at')
                   .withTimestamps();
    }

    // Has One/Many Through
    countryPhone() { return this.hasOneThrough(Phone, Country, 'country_id', 'user_id'); }
    countryPosts() { return this.hasManyThrough(Post, Country, 'country_id', 'user_id'); }

    // Polymorphic
    image()    { return this.morphOne(Image, 'imageable'); }
    comments() { return this.morphMany(Comment, 'commentable'); }
    tags()     { return this.morphToMany(Tag, 'taggable'); }
}

// Pivot table operations
await user.roles().attach([1, 2]);
await user.roles().detach([1]);
await user.roles().sync([1, 3, 5]);
await user.roles().toggle([2, 4]);

Eager Loading

const users = await User.with('posts').get();
const users = await User.with('posts', 'roles').get();
const user  = await User.with('posts:id,title').find(1);       // Select specific columns

// Nested Eager Loading (dot notation)
const users = await User.with('posts.comments.author').get();

// Querying relationship existence
const users = await User.has('posts').get();
const users = await User.doesntHave('posts').get();
const users = await User.whereHas('posts', q => q.where('published', true)).get();
const users = await User.withCount('posts').get();              // Adds posts_count

Model Events & Observers

// Register event hooks
User.creating(user => { user.slug = slugify(user.name); });
User.created(user  => { console.log('User created:', user.id); });
User.updating(user => { /* ... */ });
User.updated(user  => { /* ... */ });
User.deleting(user => { /* ... */ });
User.deleted(user  => { /* ... */ });
User.saving(user   => { /* before create or update */ });
User.saved(user    => { /* after create or update */ });

// Observer class
User.observe({
    creating(user) { /* ... */ },
    created(user)  { /* ... */ },
    updating(user) { /* ... */ },
});

Global Scopes

// Add a global scope
User.addGlobalScope('active', query => query.where('active', true));

// Query without a global scope
const allUsers = await User.withoutGlobalScope('active').get();

Model Utilities

// Instance methods
const user = await User.find(1);

user.refresh();                              // Re-fetch from DB
const copy = user.replicate(['id']);         // Clone without id
user.touch();                                // Update updated_at timestamp
user.is(anotherUser);                        // Compare by primary key
user.isNot(anotherUser);

// Dirty tracking
user.name = 'New Name';
user.isDirty();                              // true
user.isDirty('name');                        // true
user.isClean();                              // false
user.getDirty();                             // { name: 'New Name' }
user.getOriginal('name');                    // 'Old Name'

// Serialization
user.toArray();                              // Plain object (respects hidden)
user.toJSON();                               // JSON-safe object (respects hidden)
user.makeVisible(['password']);               // Temporarily unhide
user.makeHidden(['email']);                   // Temporarily hide

// Load relations explicitly (useful before passing to views)
const user = await User.find(1);
await user.load('posts', 'roles');         // Always (re)loads from DB
await user.loadMissing('comments');        // Only loads if not already loaded

// Then in your view: {{ user.posts }}, {{ user.roles }}

Soft Deletes

const SoftDeletes = use('laranode/Database/Loquent/SoftDeletes');

class Post extends SoftDeletes(Model) {
    static table = 'posts';
}

// Soft delete (sets deleted_at timestamp)
await Post.where('id', 1).delete();

// Include soft deleted records
const all = await Post.withTrashed().get();

// Restore
await Post.where('id', 1).restore();

// Force delete (permanent)
await Post.where('id', 1).forceDelete();

Authentication

LaraNode provides session-based authentication and Sanctum-style API tokens.

Session Authentication (Web)

// routes/web.js
Route.get('/login', 'AuthController@showLoginForm').name('login');
Route.post('/login', 'AuthController@login');
Route.post('/logout', 'AuthController@logout').name('logout');

// Protect routes with auth middleware
Route.group({ middleware: ['auth'] }, () => {
    Route.get('/dashboard', 'DashboardController@index');
});

API Token Authentication (Sanctum-style)

const HasApiTokens = use('laranode/Auth/Traits/HasApiTokens');

class User extends HasApiTokens(Model) {
    static table = 'users';
}

// Create a token
const token = await user.createToken('api-token');
// Returns: { plainTextToken: '1|abc123...', accessToken: {...} }

// Use in API requests
// Header: Authorization: Bearer 1|abc123...

// Protect API routes
Route.group({ middleware: ['auth:api'] }, () => {
    Route.get('/user', (req, res) => res.json(req.user));
});

Password Reset

Route.get('/password/reset', 'PasswordController@showForgotForm').name('password.request');
Route.post('/password/email', 'PasswordController@sendResetLink').name('password.email');
Route.get('/password/reset/{token}', 'PasswordController@showResetForm').name('password.reset');
Route.post('/password/reset', 'PasswordController@reset').name('password.update');

Middleware

Built-in Middleware

| Middleware | Purpose | |---|---| | TrimStrings | Trims whitespace from all input | | SecurityHeaders | Sets security HTTP headers (X-Frame-Options, etc.) | | VerifyCsrfToken | CSRF protection for web forms | | Cors | Cross-Origin Resource Sharing headers | | RateLimiter | Request rate limiting | | Authenticate | Authentication guard | | SubstituteBindings | Route model binding |

HTTP Kernel (app/Http/Kernel.js)

const Kernel = require('../../vendor/laranode/framework/src/Foundation/Http/Kernel');

class HttpKernel extends Kernel {
    constructor(app, router) {
        super(app, router);

        // Global middleware (runs on every request)
        this.middleware = [
            require('../../vendor/laranode/framework/src/Http/Middleware/TrimStrings'),
            require('../../vendor/laranode/framework/src/Http/Middleware/SecurityHeaders'),
        ];

        // Middleware groups
        this.middlewareGroups = {
            'web': ['bindings', 'csrf'],
            'api': ['bindings'],
        };

        // Named middleware (for route-level use)
        this.routeMiddleware = {
            'auth': require('../../vendor/laranode/framework/src/Auth/Middleware/Authenticate'),
            'cors': require('../../vendor/laranode/framework/src/Http/Middleware/Cors'),
            'csrf': require('../../vendor/laranode/framework/src/Http/Middleware/VerifyCsrfToken'),
            'throttle': require('../../vendor/laranode/framework/src/Http/Middleware/RateLimiter'),
        };
    }
}

Custom Middleware

// app/Http/Middleware/CheckAdmin.js
module.exports = function (req, res, next) {
    if (req.user && req.user.role === 'admin') {
        return next();
    }
    return res.status(403).send('Forbidden');
};

Validation

// Inline validation in a controller
async store(req, res) {
    const validator = new Validator(req.all(), {
        name: 'required|string|min:3|max:255',
        email: 'required|email|unique:users,email',
        password: 'required|min:8|confirmed',
        age: 'nullable|integer|min:18',
    });

    if (validator.fails()) {
        return res.status(422).json({ errors: validator.errors() });
    }

    // Validated data is safe to use
    const user = await User.create(validator.validated());
}

Available Validation Rules: required, nullable, string, integer, numeric, boolean, email, url, min, max, between, in, not_in, unique, exists, confirmed, date, alpha, alpha_num, regex, required_if, required_with, and more.


Facades

Facades provide a static-like interface to services in the container:

// Available Facades
const Route  = use('laranode/Support/Facades/Route');   // Routing
const Auth   = use('laranode/Support/Facades/Auth');     // Authentication
const DB     = use('laranode/Support/Facades/DB');       // Database
const Hash   = use('laranode/Support/Facades/Hash');     // Hashing
const Crypt  = use('laranode/Support/Facades/Crypt');    // Encryption
const Config = use('laranode/Support/Facades/Config');   // Configuration
const Log    = use('laranode/Support/Facades/Log');      // Logging
const Lang   = use('laranode/Support/Facades/Lang');     // Translation
const Http   = use('laranode/Support/Facades/Http');     // HTTP Client

HTTP Client & Macros

LaraNode features an expressive, fluent HTTP client (powered by native fetch) that is accessible via the Http Facade. It behaves identically to Laravel's HTTP Client.

const Http = use('laranode/Support/Facades/Http');

// Basic Requests
const response = await Http.get('https://api.github.com/users/octocat');
const data     = await Http.post('https://api.example.com/users', { name: 'John' });

// Response helpers
response.status();       // 200
response.successful();   // true
response.json();         // { ... }
response.header('x-ratelimit-remaining');

// Fluent configuration
await Http.withHeaders({ 'X-Custom': '123' })
          .withToken('super-secret-token')
          .acceptJson()
          .get('/endpoint');

// File Uploads (Multipart/Form-Data)
const fileBuffer = fs.readFileSync('image.png');
await Http.attach('avatar', fileBuffer, 'image.png')
          .post('/upload');

Macros

You can register macros (custom HTTP client configurations) in your AppServiceProvider and call them fluently anywhere in your application.

// app/Providers/AppServiceProvider.js
const Http = use('laranode/Support/Facades/Http');

class AppServiceProvider extends ServiceProvider {
    boot() {
        Http.macro('github', function () {
            return this.withBaseUrl('https://api.github.com')
                       .withHeaders({ 'Accept': 'application/vnd.github.v3+json' });
        });
    }
}

// In your Controllers
const response = await Http.github().get('/users/Sabbir1993');

Hashing

const Hash = use('laranode/Support/Facades/Hash');

// Hash a password
const hashed = await Hash.make('my-password');

// Verify a password
const valid = await Hash.check('my-password', hashed); // true

Encryption

const Crypt = use('laranode/Support/Facades/Crypt');

// Encrypt
const encrypted = Crypt.encrypt('sensitive-data');

// Decrypt
const decrypted = Crypt.decrypt(encrypted);

Logging

const Log = use('laranode/Support/Facades/Log');

Log.info('User logged in', { userId: 1 });
Log.warning('Disk space low');
Log.error('Payment failed', { orderId: 123 });

Views (Edge Templating)

LaraNode uses Edge templates (.edge files) — a Blade-like engine:

<!-- resources/views/layouts/app.edge -->
<!DOCTYPE html>
<html>
<head>
    <title>@yield('title') - {{ config('app.name') }}</title>
</head>
<body>
    @section('content')
    @endsection
</body>
</html>
<!-- resources/views/welcome.edge -->
@extends('layouts.app')

@section('title', 'Home')

@section('content')
    <h1>Welcome, {{ auth.user.name }}!</h1>

    @if(auth.user.isAdmin)
        <p>Admin Panel</p>
    @endif

    @foreach(todos as todo)
        <div>{{ todo.title }} - {{ todo.completed ? '✓' : '✗' }}</div>
    @endforeach
@endsection
// Render a view from a controller
async index(req, res) {
    return res.view('welcome', {
        user: req.user,
        todos: await Todo.all()
    });
}

Configuration

Configuration files in config/ use environment variables:

// config/app.js
module.exports = {
    name: env('APP_NAME', 'LaraNode'),
    env: env('APP_ENV', 'production'),
    debug: env('APP_DEBUG', false),
    url: env('APP_URL', 'http://localhost'),
    port: env('APP_PORT', 8000),
    key: env('APP_KEY'),
    providers: [
        // Service providers loaded at boot
    ],
};
# .env
APP_NAME=LaraNode
APP_ENV=local
APP_DEBUG=true
APP_PORT=3333

DB_CONNECTION=sqlite
DB_DATABASE=laranode

Database

Supported Databases

  • SQLite — zero-config, great for development
  • MySQL — production-ready with connection pooling
// config/database.js
module.exports = {
    default: env('DB_CONNECTION', 'sqlite'),
    connections: {
        sqlite: {
            driver: 'sqlite',
            database: env('DB_DATABASE', database_path('database.sqlite')),
        },
        mysql: {
            driver: 'mysql',
            host: env('DB_HOST', '127.0.0.1'),
            port: env('DB_PORT', '3306'),
            database: env('DB_DATABASE', 'laranode'),
            username: env('DB_USERNAME', 'root'),
            password: env('DB_PASSWORD', ''),
        },
    }
};

Raw Queries via DB Facade

const DB = use('laranode/Support/Facades/DB');

const users = await DB.table('users').where('active', true).get();
const result = await DB.raw('SELECT * FROM users WHERE id = ?', [1]);

Pagination

LaraNode provides two types of paginators — paginate() for full page-number navigation and simplePaginate() for lightweight previous/next.

Length-Aware Pagination (paginate)

// In a controller
async index(req, res) {
    const page = parseInt(req.query.page) || 1;
    const users = await User.where('active', true).paginate(15, page);

    // For API responses
    return res.json(users.toArray());
    // Returns: { data: [...], meta: { current_page, last_page, per_page, total } }

    // For views — render pagination links as HTML
    return res.view('users.index', {
        users: users.items,
        paginationLinks: users.links('/users?')
    });
}
<!-- In your Edge template -->
@foreach(users as user)
    <div>{{ user.name }}</div>
@endforeach

{{{ paginationLinks }}}

Simple Pagination (simplePaginate)

For large datasets where you don't need the total count:

async index(req, res) {
    const page = parseInt(req.query.page) || 1;
    const users = await User.simplePaginate(15, page);

    return res.view('users.index', {
        users: users.items,
        paginationLinks: users.links('/users?')
    });
}

Pagination Methods

| Method | Description | |---|---| | paginate(perPage, page) | Full pagination with total count and page numbers | | simplePaginate(perPage, page) | Lightweight previous/next only | | .items | The array of items for the current page | | .total | Total number of records (paginate only) | | .currentPage | Current page number | | .lastPage | Last page number (paginate only) | | .hasMorePages() | Whether more pages exist | | .toArray() | Returns { data, meta } object for API responses | | .links(path) | Renders Bootstrap-compatible pagination HTML |


Queues

LaraNode includes a database-driven queue system for processing jobs in the background, similar to Laravel.

Creating a Job

Create job classes in app/Jobs/:

const Job = use('laranode/Queue/Job');

class ProcessPodcast extends Job {
    constructor(data) {
        super(data);
    }

    async handle() {
        console.log('Processing podcast:', this.data.podcastId);
        // Your job logic here...
    }
}

module.exports = ProcessPodcast;

Dispatching Jobs

const ProcessPodcast = use('App/Jobs/ProcessPodcast');

// Basic dispatch
await ProcessPodcast.dispatch({ podcastId: 1 });

// Dispatch with delay (in seconds)
await ProcessPodcast.dispatch({ podcastId: 1 }).delay(600); // 10 minutes

// Dispatch to a specific queue
await ProcessPodcast.dispatch({ podcastId: 1 }).onQueue('high');

// Chained
await ProcessPodcast.dispatch({ podcastId: 1 }).onQueue('high').delay(60);

Running the Queue Worker

# Process jobs on the default queue
node artisan queue:work

# Process a specific queue
node artisan queue:work --queue=high

# Customize sleep interval and max retries
node artisan queue:work --sleep=1 --tries=5

Failed Jobs

When a job exceeds the maximum number of attempts (--tries), it is automatically moved to the failed_jobs table with the exception trace, queue name, payload, and a UUID.

Required Migrations

Run node artisan migrate to create the jobs and failed_jobs tables.


Broadcasting

Powered by Socket.io, LaraNode's broadcasting allows you to share server-side events with your client-side JavaScript application in real-time.

// app/Events/OrderShipped.js
class OrderShipped extends ShouldBroadcast {
    constructor(order) {
        super();
        this.order = order;
    }

    broadcastOn() {
        return ['orders', `user.${this.order.user_id}`];
    }
}

// In your controller
Event.dispatch(new OrderShipped(order));

Testing

LaraNode features a robust testing framework inspired by Laravel. It supports artisan test and provides a TestCase class for building expressive feature tests.

// tests/Feature/UserTest.js
const TestCase = use('laranode/Foundation/Testing/TestCase');

class UserTest extends TestCase {
    async test_can_visit_home_page() {
        const response = await this.get('/');
        response.assertStatus(200);
        response.assertSee('LaraNode');
    }
}

Running Tests

node artisan test

Performance

Optimize your application for production by caching performance-heavy components.

# Cache configuration
node artisan config:cache

# Cache routes
node artisan route:cache

Service Providers

Register services in config/app.js:

providers: [
    // Framework providers
    require(base_path('vendor/laranode/framework/src/Foundation/Providers/RouteServiceProvider')),
    require(base_path('vendor/laranode/framework/src/Foundation/Providers/DatabaseServiceProvider')),
    require(base_path('vendor/laranode/framework/src/Log/LogServiceProvider')),
    require(base_path('vendor/laranode/framework/src/View/ViewServiceProvider')),
    require(base_path('vendor/laranode/framework/src/Translation/TranslationServiceProvider')),
    require(base_path('vendor/laranode/framework/src/Auth/AuthServiceProvider')),
    require(base_path('vendor/laranode/framework/src/Hashing/HashServiceProvider')),
    require(base_path('vendor/laranode/framework/src/Encryption/EncryptionServiceProvider')),

    // Your custom providers
    // require(base_path('app/Providers/AppServiceProvider')),
],

Exception Handling

Customize error handling in app/Exceptions/Handler.js:

const BaseHandler = use('laranode/Foundation/Exceptions/Handler');

class Handler extends BaseHandler {
    constructor(app) {
        super(app);
        this.dontReport = [
            // Exceptions that should not be logged
        ];
    }

    register() {
        // Custom renderable
        this.renderable((error, req, res) => {
            if (error.name === 'PaymentException') {
                return res.status(402).json({ message: 'Payment required' });
            }
        });

        // Custom reportable
        this.reportable((error) => {
            // Send to Sentry, Datadog, etc.
        });
    }
}

Log Viewer

LaraNode includes a built-in web-based log viewer with severity filtering:

// config/logging.js
module.exports = {
    allow_log_viewer: true,
    log_viewer: {
        middleware: ['web'],
        endpoint: '/logs',
    },
};

Visit /logs to view application logs with:

  • File browser sidebar
  • Severity filters (Error, Warning, Info, Debug)
  • Expandable rows for full stack traces
  • Chunk-based pagination for large files

Artisan CLI

The LaraNode Artisan CLI provides commands for scaffolding, database management, and running your application.

# Start the development server
node artisan serve

# Start with a custom port
node artisan serve --port=8080

# Run scheduled commands
node artisan schedule:run

Available Commands

Here is a full list of available Artisan commands:

| Command | Description | Usage | |---|---|---| | Development | | | | serve | Serve the application on the local development server | node artisan serve [options] | | route:list | List all registered routes | node artisan route:list [options] | | Make (Scaffolding) | | | | make:controller | Create a new controller class | node artisan make:controller [options] <name> | | make:model | Create a new Loquent model class | node artisan make:model [options] <name> | | make:middleware | Create a new middleware class | node artisan make:middleware <name> | | make:migration | Create a new migration file | node artisan make:migration <name> | | make:seeder | Create a new seeder class | node artisan make:seeder <name> | | make:request | Create a new form request class | node artisan make:request <name> | | make:resource | Create a new API resource class | node artisan make:resource [options] <name> | | make:event | Create a new event class | node artisan make:event <name> | | make:listener | Create a new event listener class | node artisan make:listener <name> | | make:mail | Create a new mailable class | node artisan make:mail <name> | | make:notification| Create a new notification class | node artisan make:notification <name> | | make:command | Create a new Artisan command | node artisan make:command <name> | | make:rule | Create a new custom validation rule class | node artisan make:rule <name> | | Database | | | | migrate | Run the database migrations | node artisan migrate | | migrate:rollback| Rollback the last database migration | node artisan migrate:rollback | | migrate:fresh | Drop all tables and re-run all migrations | node artisan migrate:fresh [options] | | db:seed | Seed the database with records | node artisan db:seed [options] | | System | | | | schedule:run | Run the scheduled commands | node artisan schedule:run | | queue:work | Start processing jobs on the queue | node artisan queue:work [options] | | help | Display help for a specific command | node artisan help [command] |

Note: You can run node artisan help <command> for more detailed usage and available options for any specific command.


Events & Listeners

LaraNode provides a robust event dispatching system similar to Laravel. Listeners are automatically resolved through the service container.

const Event = use('laranode/Support/Facades/Event');

// Dispatch an event
Event.dispatch('OrderShipped', order);

// Listen to an event
Event.listen('OrderShipped', SendShipmentNotification);

Register your listeners in app/Providers/EventServiceProvider.js.


Cache

The LaraNode Cache system provides a unified API for various cache drivers. Currently supported drivers: file and memory.

const Cache = use('laranode/Support/Facades/Cache');

// Store an item
await Cache.put('key', 'value', 60);

// Retrieve an item
const value = await Cache.get('key');

// Retrieve & store if empty
const users = await Cache.remember('users', 60, async () => {
    return await DB.table('users').get();
});

Storage / Filesystem

LaraNode provides a powerful filesystem abstraction matching Laravel's Storage API.

const Storage = use('laranode/Support/Facades/Storage');

// Store a file with visibility
await Storage.put('avatars/1.jpg', fileData, 'public');

// Get URL (requires storage:link)
const url = Storage.url('avatars/1.jpg'); // '/storage/avatars/1.jpg'

// Get Mime Type
const type = await Storage.mimeType('document.pdf'); // 'application/pdf'

// Create symbolic link
// node artisan storage:link

// Read a file
const contents = await Storage.get('file.txt');

// Delete a file
await Storage.delete('file.txt');

// Verify existence
if (await Storage.exists('file.txt')) { ... }

#### File Uploads

LaraNode provides a convenient wrapper for uploaded files with a Laravel-compatible API.

```javascript
async upload(req, res) {
    const file = req.file('avatar'); // Returns UploadedFile instance

    if (file) {
        // Get file info
        const name = file.getClientOriginalName();
        const ext  = file.getClientOriginalExtension();
        const size = file.getSize();

        // Store the file (returns the generated path)
        const path = await file.store('avatars');
        
        return res.json({ path });
    }
}

---

### Mail

LaraNode's Mail system, built on top of Nodemailer, allows for expressive email creation via Mailable classes.

```javascript
const Mail = use('laranode/Support/Facades/Mail');

// Send a mailable
await Mail.to(user.email).send(new WelcomeMail(user));

// If Mailable implements ShouldQueue, it's sent in the background automatically!
class WelcomeMail extends Mailable implements ShouldQueue { ... }
// A sample Mailable class
class WelcomeMail extends Mailable {
    constructor(user) { super(); this.user = user; }
    build() {
        return this.from('[email protected]')
                   .subject('Welcome!')
                   .view('emails.welcome', { user: this.user });
    }
}

Notifications

LaraNode allows you to send notifications across various channels (Mail, Database).

// Notify an entity
await Notification.send(user, new InvoicePaid(invoice));
// A sample Notification class
class InvoicePaid extends Notification {
    via(notifiable) { return ['mail', 'database']; }

    toMail(notifiable) {
        return new MailMessage()
            .greeting('Hello!')
            .line('Your invoice has been paid.')
            .action('View Invoice', invoice.url);
    }

    toDatabase(notifiable) {
        return { invoice_id: this.invoice.id };
    }

    toBroadcast(notifiable) {
        return { amount: this.invoice.total };
    }
}

// Any model can receive notifications using the Notifiable mixin
class User extends Notifiable(Model) { ... }
await user.notify(new InvoicePaid(invoice));

Helper Functions

LaraNode provides global helper functions:

env('APP_NAME', 'default')        // Read environment variable
base_path('storage/logs')         // Absolute path from project root
database_path('database.sqlite')  // Path to database/ directory
config('app.name')                // Read config value
use('App/Models/User')            // Resolve from service container

Environment Variables

| Variable | Description | Default | |---|---|---| | APP_NAME | Application name | LaraNode | | APP_ENV | Environment (local, production) | local | | APP_KEY | Encryption key (auto-generated) | — | | APP_DEBUG | Debug mode | true | | APP_PORT | Server port | 3333 | | APP_URL | Application URL | http://localhost | | APP_LOCALE | Default locale | en | | LOG_CHANNEL | Log channel | stack | | LOG_LEVEL | Minimum log level | debug | | DB_CONNECTION | Database driver | sqlite | | DB_HOST | Database host | 127.0.0.1 | | DB_PORT | Database port | 3306 | | DB_DATABASE | Database name | laranode | | DB_USERNAME | Database user | root | | DB_PASSWORD | Database password | — |


Sample Application

The scaffolded project includes a complete sample application:

  • Authentication — Login, Register, Logout, Password Reset
  • Todo CRUD — Full create, read, update, delete with API authentication
  • API EndpointsGET /api/user for testing API access

Sample Routes

// Web routes (routes/web.js)
Route.get('/login', 'AuthController@showLoginForm');
Route.post('/login', 'AuthController@login');
Route.get('/register', 'AuthController@showRegisterForm');
Route.post('/register', 'AuthController@register');

// Protected CRUD routes
Route.group({ middleware: ['auth:api'] }, () => {
    Route.get('/todos', 'TodoController@index');
    Route.post('/todos', 'TodoController@store');
    Route.put('/todos/{todo}', 'TodoController@update');
    Route.delete('/todos/{todo}', 'TodoController@destroy');
});

// API routes (routes/api.js)
Route.get('/user', async (req, res) => {
    const user = await User.first();
    return res.json({ user });
});

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

The LaraNode framework is open-sourced software licensed under the MIT license.