Skip to main content

Routing

Signature​

All routes in CrumbJS follow the same signature:

// Route signature
app[method](path, handler, options?);

app.get("/path", handler, routeConfig);
app.post("/path", handler, routeConfig);
app.put("/path", handler, routeConfig);
app.patch("/path", handler, routeConfig);
app.delete("/path", handler, routeConfig);
app.options("/path", handler, routeConfig);
app.head("/path", handler, routeConfig);

app.proxy(method, localPath, dest, routeConfig);
app.proxyAll(localPath, dest, use);
app.static(path, content, type)

Wildcards and params​

// Basic param usage (inferred as string)
app.get("/user/:id", ({ params }) => {
return `User id is: ${params.id}`; // is type-safe too without defining schema
});

// Defining validation rules for the path params
app.get("/user/:id", ({ params }) => {
return `User id is: ${params.id}`;
}, {
params: z.object({ // must be a Zod Object
id: z.string().min(36), // must define rules to all index without ':'
})
});

// Some advanced things you can do with Path Params + Zod + Drizzle
app.get(
"/user/:user/posts",
async ({ params }) => {
return params.user.name; // return user name from "db"
},
{
// If `params` is defined, you must describe ALL path params
params: z.object({
user: z.string().transform(async (val) => {
const user = await db.query.users.findFirst({
where: eq(users.id, val)
});
if (!user) throw new NotFound();
return user;
}),
}),
}
);

// Wildcard (match everything after /user/)
app.get("/user/*", ({ params }) => {
return `Matched path: ${params["*"]}`;
});

Proxy​

CrumbJS provides built-in support for proxying requests to other services.
This is useful for API gateways, authentication forwarding, or integrating multiple microservices.


πŸ”Ή Single Proxy​

Use app.proxy() to forward a specific method and path to a target URL.
These routes may be fully documented with OpenAPI, including validation and middlewares.

app.proxy(
'POST',
'/auth',
'https://auth-ms.example.com/v1/auth',
{ body: authRequestDto, use: [guestMiddleware] }
);

πŸ”Ή Proxy All Methods​

Use app.proxyAll() to forward all HTTP methods under a path prefix.
This is useful when you don’t need per-route documentation.

⚠️ Note: These routes cannot be pre-validated or documented with OpenAPI, but you can still attach middlewares.

app.proxyAll('/v1', 'https://microservice-a.example.com/v1', [authMiddleware]);
// Example: '/v1/any/path' β†’ https://microservice-a.example.com/v1/any/path

app.proxyAll('/v2', 'https://microservice-a.example.com/v2', [authMiddleware]);
// Example: '/v2/any/path' β†’ https://microservice-a.example.com/v2/any/path

Key Points​

  • proxy() β†’ Single method + path, may be validated with zod, documented with OpenAPI and use middleware/s.
  • proxyAll() β†’ All methods + prefix forwarding, no validation and not documentable with OpenAPI, but supports middleware/s.

Static Routes​

Static routes allow you to serve files or strings directly from memory at application startup.
This is useful for serving assets (PDFs, HTML snippets, images) without setting up a full static file server.


πŸ”Ή Content Loading​

  • Files (Blob): The file is read into memory on startup.
    • If no Content-Type is provided, it will be inferred from the Blob.
  • Strings: Sent as-is.
    • If no Content-Type is provided, it defaults to application/octet-stream.

πŸ”Ή Reference​

For more details on Bun’s static responses, check the official docs:
πŸ‘‰ Bun.serve - static-responses


πŸ”Ή Usage​

/** 
* Signature
* static(path: string, content: string | Blob, type?: ContentType)
*/

// Serve a file, type inferred from Blob
app.static('/document.pdf', Bun.file('/path/to/file.pdf'));
// Serve a file but force download (octet-stream)
app.static('/document.pdf', Bun.file('/path/to/file.pdf'), 'application/octet-stream');
// Serve inline HTML
app.static('/content', '<html><body>Hello</body></html>', 'text/html');
  • All static content is eagerly loaded into memory at application startup.
  • Best suited for small to medium FILES or predefined responses (e.g. logos, HTML snippets, config files).
  • ⚠️ Not recommended for very large FILES (e.g. files >500 MB), since this approach keeps them entirely in memory.
    For such cases, consider create a streaming route or use a dedicated static file server instead.

Handlers​

A route handler in CrumbJS receives a single argument: the Context object.
Context is a rich object that contains everything you need to work with the request and response.
We will dive deeper into Context in the next section.

The return value of a handler must be one of the following:

  • an object (automatically serialized to JSON),
  • null, or
  • a Response instance.
app.get("/path-to-route", async (ctx) => {
return { message: "Hello World" }; // object, null, or Response
});