Skip to content

Minima.jsThoughtfully Designed for Modern Runtimes

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

Minima.js

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

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 { 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 default function (app) {
  app.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 } 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 */
  ]);
}
typescript
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:

  • ✅ 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)

REST API with Auth

src/
├── module.ts           # Global auth, body parsing, CORS
├── auth/
│   └── module.ts       # POST /auth/login (public)
└── users/
    └── module.ts       # GET/POST /users/* (protected)