Built from Scratch for Modern Runtimes
Not a legacy port. Designed ground-up for Bun and Node.js with native APIs, zero compatibility layers, zero historical baggage.
Most frameworks optimize features. Minima.js optimizes how it feels to work every day.
Watch how little code you need to write. Notice what you DON'T see—no imports, no registration, no wiring.
import { createApp } from "@minimajs/server/bun";
// import { createApp } from "@minimajs/server/node"; // for node
const app = createApp();
await app.listen({ port: 3000 });
// That's your entire entry pointimport { type Meta } from "@minimajs/server";
import { cors } from "@minimajs/server/plugins";
// Global config - applies to every route
export const meta: Meta = {
prefix: "/api",
plugins: [cors()],
};
// sync / async supported
export default async function (app) {
app.get("/health", () => ({ status: "ok" }));
}// Auto-loaded as /api/users/*
import { body } from "@minimajs/server";
function getUsers() {
return [
{ id: 1, name: "Alice" },
{ id: 2, name: "Bob" },
];
}
function createUser() {
const user = body();
return { created: user };
}
export default function (app) {
app.get("/list", getUsers);
app.post("/create", createUser);
}// Auto-loaded as /api/posts/*
function getLatestPosts() {
return { posts: [] };
}
export default function (app) {
app.get("/latest", getLatestPosts);
}Your API is ready:
GET /api/health → {"status":"ok"}GET /api/users/list → [{"id":1,"name":"Alice"}...]POST /api/users/create → Creates userGET /api/posts/latest → {"posts":[]}Protected routes? Just add a plugin to meta.plugins:
import { type Meta } from "@minimajs/server";
import { authPlugin, guardPlugin, getUser } from "../auth/index.js";
export const meta: Meta = {
plugins: [
authPlugin, // Makes getUser() available
guardPlugin, // Requires authentication
],
};
export default async function (app) {
app.get("/profile", () => {
const user = getUser(); // Guaranteed to exist (guard ensures it)
return { user };
});
}No decorators. No middleware chains. Just declare what you need.
See full JWT authentication tutorial →
Each module creates an isolated scope. Plugins, hooks, and configuration stay contained—no accidental global state, no sibling interference.
import { type Meta } from "@minimajs/server";
import { cors } from "@minimajs/server/plugins";
// Root module - these plugins apply to ALL children
export const meta: Meta = {
prefix: "/api",
plugins: [cors()],
};import { type Meta } from "@minimajs/server";
import { hook } from "@minimajs/server";
// Users module - this hook ONLY affects /api/users/* routes
export const meta: Meta = {
plugins: [hook("request", () => console.log("Users accessed"))],
};
export default async function (app) {
app.get("/list", () => [
/* users */
]);
}import { searchParams } from "@minimajs/server";
// Posts module - no logging hook here
// Completely isolated from users module
function getPosts() {
// contexts will be available everywhere
const page = searchParams.get("page", Number); // cast page to number
return {
page,
data: [], // posts
};
}
export default async function (app) {
app.get("/latest", getPosts);
}How it works:
Request to /api/users/list:
→ Root plugins run (cors)
→ Users plugins run (logging hook)
→ Route handler executesRequest to /api/posts/latest:
→ Root plugins run (cors)
→ Route handler executes
✅ Users logging hook DOES NOT run (isolated)src/
├── module.ts # Global auth, body parsing, CORS
├── auth/
│ └── module.ts # POST /auth/login (public)
└── users/
└── module.ts # GET/POST /users/* (protected)Minima.js removes the friction you’ve learned to tolerate—slow feedback, noisy types, hidden lifecycles, and tangled modules—so building backends feels fast, clear, and predictable again.
Get Started →Open source and community-driven. Report bugs, request features, or contribute code. We'd love your feedback.
GitHub →