Getting Started with Minima.js
This guide introduces the core concepts of Minima.js to get you up and running quickly. We'll start with a minimal application and then explore the key features that make the framework powerful and elegant.
New to Minima.js? Check out the Introduction for a conceptual overview before diving into this tutorial.
Setup
First, choose your runtime and create a new project directory.
Option 1: Bun
mkdir minimajs-app
cd minimajs-app
bun init -y
bun add @minimajs/serverThen start your server:
bun --watch src/index.ts # development with auto-reload
bun src/index.ts # productionOption 2: Node.js
mkdir minimajs-app
cd minimajs-app
npm init -y
npm install @minimajs/server
npm install -D typescript tsx @types/nodeUpdate your package.json to enable ES modules and add start scripts:
{
"type": "module",
"scripts": {
"start": "tsx src/index.ts",
"dev": "tsx watch src/index.ts"
}
}Create a tsconfig.json for TypeScript support:
{
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "bundler",
"strict": true
}
}Then start your server:
npm run dev # development with auto-reload
npm run start # productionChoose Your Runtime
Minima.js is optimized for both runtimes. You select your target by changing the import path:
@minimajs/server/bun: Uses Bun's native, high-performanceBun.serve().@minimajs/server/node: Uses Node.js's standardhttp.createServer().@minimajs/server: Defaults to the Node.js runtime.
This provides native performance with zero abstraction overhead.
A Minimal Application
Create a src/index.ts file. Here is a very basic Minima.js application:
import { createApp } from "@minimajs/server/bun";
import { params } from "@minimajs/server";
const app = createApp();
// Simple functional route
app.get("/", () => ({ message: "Hello, World!" }));
// Demonstrates context-aware access to route parameters
app.get("/hello/:name", () => {
const name = params.get("name");
return { message: `Hello, ${name}!` };
});
const address = await app.listen({ port: 3000 });
console.log(`Server listening on ${address}`);import { createApp } from "@minimajs/server/node";
import { params } from "@minimajs/server";
const app = createApp();
// Simple functional route
app.get("/", () => ({ message: "Hello, World!" }));
// Demonstrates context-aware access to route parameters
app.get("/hello/:name", () => {
const name = params.get("name");
return { message: `Hello, ${name}!` };
});
const address = await app.listen({ port: 3000 });
console.log(`Server listening on ${address}`);This short example already showcases several core concepts. Let's build on this foundation.
Access Request Data Anywhere
Notice we imported params and used it directly in the route handler without it being passed as an argument:
import { params } from "@minimajs/server";
app.get("/hello/:name", () => {
const name = params.get("name"); // ✅ No req.params.name
return { message: `Hello, ${name}!` };
});Available context functions: request, response, params, body, headers, searchParams
For more details, see the Http Helpers Guide.
Organize with File-Based Modules
As your application grows, organize routes by creating module.ts files. They're auto-discovered based on folder structure:
src/
├── index.ts # Entry point
├── users/
│ └── module.ts # → /users/*
└── posts/
└── module.ts # → /posts/*import { createApp } from "@minimajs/server";
const app = createApp(); // Auto-discovers modules!
await app.listen({ port: 3000 });import { params } from "@minimajs/server";
import type { Routes } from "@minimajs/server";
function listUsers() {
return [{ id: 1, name: "John" }];
}
function getUser() {
const id = params.get("id");
return { id, name: "John" };
}
export const routes: Routes = {
"GET /list": listUsers,
"GET /:id": getUser,
};Your API is ready:
GET /users/listGET /users/:idGET /posts/list
Want to add plugins to a module? Use meta.plugins:
import { type Meta, type Routes, hook } from "@minimajs/server";
export const meta: Meta = {
plugins: [hook("request", () => console.log("User route accessed"))],
};
function listUsers() {
return [
/* users */
];
}
export const routes: Routes = {
"GET /list": listUsers,
};Learn more: Module Tutorial
Add Lifecycle Hooks
Use hooks to tap into request/app lifecycle events. Perfect for logging, auth, error handling:
import { createApp } from "@minimajs/server";
import { hook } from "@minimajs/server";
const app = createApp();
// Log every request
app.register(
hook("request", ({ request, pathname }) => {
console.log(`[REQ] ${request.method} ${pathname}`);
})
);Common hooks: request, transform, send, error, hook.lifespan
Learn more: Hooks Guide
Handle Errors Centrally
Use an error hook to catch all errors in one place:
import { createApp } from "@minimajs/server";
import { hook, abort } from "@minimajs/server";
const app = createApp();
app.register(
hook("error", (error) => {
console.error("Error:", error.message);
abort("Something went wrong!", { status: 500 });
})
);Learn more: Error Handling Guide
Next Steps
You now have a working Minima.js application! Here's what to explore next:
- Task Board Tutorial - Build a complete REST API from scratch
- Module Tutorial - Step-by-step guide to structuring your app
- JWT Authentication Recipe - Add auth in 5 minutes
- Hooks Guide - Master the lifecycle system
- Routing Guide - Advanced routing patterns