Web Standards, Pure ESM, Zero Abstractions
Built on Web APIs you already know—Request, Response, File, Blob, URL. No proprietary abstractions. Your code stays portable and future-proof.
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, Routes } from "@minimajs/server";
import { cors } from "@minimajs/server/plugins";
// Global config - applies to every route
export const meta: Meta = {
prefix: "/api",
plugins: [cors()],
};
function getHealth() {
return { status: "ok" };
}
export const routes: Routes = {
"GET /health": getHealth,
};// Auto-loaded as /api/users/*
import type { Routes } from "@minimajs/server";
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 const routes: Routes = {
"GET /list": getUsers,
"POST /create": createUser,
};// Auto-loaded as /api/posts/*
import type { Routes } from "@minimajs/server";
function getLatestPosts() {
return { posts: [] };
}
export const routes: Routes = {
"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":[]}Upload handling with @minimajs/multipart gives you native File instances—no custom wrappers, no learning curve.
import type { Routes } from "@minimajs/server";
import { multipart, helpers } from "@minimajs/multipart";
export async function uploadAvatar() {
// Returns native File instance - holds data in memory
const avatar = await multipart.file("avatar");
// Or use streaming without memory overhead
// const avatar = streaming.file("avatar");
// Move file to destination
await helpers.save(avatar, "./uploads/avatars");
// File is a valid Response - renders with correct content-type
return avatar;
}
export const routes: Routes = {
"POST /avatar": uploadAvatar,
};What you get:
File instances (Web Standards API)multipart.file() reads entire file into memoryFile works as Response automatically@minimajs/multipart/schema Zod guards your uploads, disk handles the weightEach 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, Routes } 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"))],
};
function listUsers() {
return [
/* users */
];
}
export const routes: Routes = {
"GET /list": listUsers,
};import type { Routes } from "@minimajs/server";
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 const routes: Routes = {
"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 →