Skip to content

Minima.jsBackend Framework for Bun and Node.js

Build APIs with Web standards, file-based modules, and context-aware helpers that reduce boilerplate.

Minima.js

Try It Now

bash
bunx @minimajs/cli new my-app --bun && cd my-app && ./app dev
bash
npx @minimajs/cli new my-app && cd my-app && ./app dev

That's it — a scaffolded project with TypeScript, file-based routing, and a running dev server.

Start Here

How It Feels to Build

Watch how little code you need to write. Notice what you DON'T see—no imports, no registration, no wiring.

typescript
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 point
typescript
import 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,
};
typescript
// 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,
};
typescript
// 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 user
  • GET /api/posts/latest{"posts":[]}

Most frameworks optimize features.
Minima.js optimizes how it feels to work every day.

Handle File Uploads with Native File API

Upload handling with @minimajs/multipart gives you native File instances—no custom wrappers, no learning curve.

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

  • Native File instances (Web Standards API)
  • multipart.file() reads entire file into memory
  • File works as Response automatically
  • Use @minimajs/multipart/schema Zod guards your uploads, disk handles the weight

See multipart documentation →

True Module Encapsulation

Each module creates an isolated scope. Plugins, hooks, and configuration stay contained—no accidental global state, no sibling interference.

typescript
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()],
};
typescript
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,
};
typescript
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:

  • ✅ Root module plugins → Inherited by all children
  • ✅ Parent module plugins → Inherited by their children only
  • ✅ Sibling modules → Completely isolated from each other
  • ✅ Child can override or extend parent behavior
  • ✅ No global state pollution

Request to /api/users/list:

→ Root plugins run (cors)
→ Users plugins run (logging hook)
→ Route handler executes

Request to /api/posts/latest:

→ Root plugins run (cors)
→ Route handler executes
✅ Users logging hook DOES NOT run (isolated)

Build Next

Choose the path that matches what you need right now:

Community

Minima.js is open-source and community-driven.

If you are evaluating frameworks right now, start with Getting Started and build one real route module before deciding.