Skip to content

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

bash
mkdir minimajs-app
cd minimajs-app
bun init -y
bun add @minimajs/server

Then start your server:

bash
bun --watch src/index.ts  # development with auto-reload
bun src/index.ts          # production

Option 2: Node.js

bash
mkdir minimajs-app
cd minimajs-app
npm init -y
npm install @minimajs/server
npm install -D typescript tsx @types/node

Update your package.json to enable ES modules and add start scripts:

json
{
  "type": "module",
  "scripts": {
    "start": "tsx src/index.ts",
    "dev": "tsx watch src/index.ts"
  }
}

Create a tsconfig.json for TypeScript support:

json
{
  "compilerOptions": {
    "target": "ESNext",
    "module": "ESNext",
    "moduleResolution": "bundler",
    "strict": true
  }
}

Then start your server:

bash
npm run dev    # development with auto-reload
npm run start  # production

Choose 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-performance Bun.serve().
  • @minimajs/server/node: Uses Node.js's standard http.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:

typescript
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}`);
typescript
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:

typescript
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/*
typescript
import { createApp } from "@minimajs/server";

const app = createApp(); // Auto-discovers modules!

await app.listen({ port: 3000 });
typescript
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/list
  • GET /users/:id
  • GET /posts/list

Want to add plugins to a module? Use meta.plugins:

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

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

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