Installation
bun add elysia @elysiajs/cors @elysiajs/jwt
Import the Elysia integration:
import {
registerElysiaRoutes,
onionErrorHandler,
type ElysiaContext,
type ElysiaMiddleware,
} from '@cosmneo/onion-lasagna/http/frameworks/elysia';
Quick Start
import { Elysia } from 'elysia';
import { registerElysiaRoutes, onionErrorHandler } from '@cosmneo/onion-lasagna/http/frameworks/elysia';
import { bootstrapProjectManagement } from './bootstrap';
const { routes } = bootstrapProjectManagement();
const app = new Elysia()
.onError(({ error }) => onionErrorHandler({ error }))
.get('/health', () => ({ status: 'ok' }));
registerElysiaRoutes(app, routes);
app.listen(3000);
Registering Routes
The registerElysiaRoutes function registers routes with your Elysia app:
import { serverRoutes } from '@cosmneo/onion-lasagna/http/server';
import { registerElysiaRoutes } from '@cosmneo/onion-lasagna/http/frameworks/elysia';
// Create routes using the builder
const routes = serverRoutes(userRouter)
.handle('users.get', {
requestMapper: (req) => ({ userId: req.pathParams.userId }),
useCase: getUserQuery,
responseMapper: (output) => ({ status: 200 as const, body: output }),
})
.build();
// Register with Elysia
registerElysiaRoutes(app, routes);
Info:
Note: registerElysiaRoutes is a function that mutates the app, not a plugin. Call it with app as the first argument.
Options
interface RegisterElysiaRoutesOptions {
middlewares?: ElysiaMiddleware[]; // Middlewares applied to all routes
contextExtractor?: ElysiaContextExtractor; // Extract auth context
}
registerElysiaRoutes(app, routes, {
middlewares: [authMiddleware],
contextExtractor: (ctx) => ({
userId: ctx.store['userId'],
}),
});
Context Extraction (Protected Routes)
For authenticated routes, provide a contextExtractor to pass auth data to handlers:
import type { ElysiaContext, ElysiaMiddleware } from '@cosmneo/onion-lasagna/http/frameworks/elysia';
// Auth middleware
const authMiddleware: ElysiaMiddleware = async (ctx: ElysiaContext) => {
const authHeader = ctx.headers['authorization'];
if (!authHeader?.startsWith('Bearer ')) {
return new Response(JSON.stringify({ message: 'Unauthorized' }), {
status: 401,
headers: { 'Content-Type': 'application/json' },
});
}
const token = authHeader.slice(7);
const payload = decodeJwt(token);
ctx.store['userId'] = payload.sub;
return undefined; // Continue to handler
};
// Register with context extraction
registerElysiaRoutes(app, routes, {
middlewares: [authMiddleware],
contextExtractor: (ctx: ElysiaContext) => ({
userId: ctx.store['userId'] as string,
}),
});
Error Handler
The onionErrorHandler maps domain errors to HTTP responses:
app.onError(({ error }) => onionErrorHandler({ error }));
Error Mapping
| Error Type | HTTP Status | Body |
|---|---|---|
ObjectValidationError | 400 | { errorCode, message, details } |
InvalidRequestError | 400 | { errorCode, message, details } |
UseCaseError | 400 | { errorCode, message } |
AccessDeniedError | 403 | { errorCode, message } |
NotFoundError | 404 | { errorCode, message } |
ConflictError | 409 | { errorCode, message } |
UnprocessableError | 422 | { errorCode, message } |
DomainError | 500 | Masked |
InfraError | 500 | Masked |
| Unknown | 500 | Masked |
Complete Example
import 'dotenv/config';
import { Elysia } from 'elysia';
import { cors } from '@elysiajs/cors';
import {
registerElysiaRoutes,
onionErrorHandler,
type ElysiaContext,
type ElysiaMiddleware,
} from '@cosmneo/onion-lasagna/http/frameworks/elysia';
import { bootstrapProjectManagement } from './bootstrap';
const { routes } = bootstrapProjectManagement();
// Auth middleware
const authMiddleware: ElysiaMiddleware = async (ctx: ElysiaContext) => {
const authHeader = ctx.headers['authorization'];
if (!authHeader?.startsWith('Bearer ')) {
return new Response(JSON.stringify({ message: 'Unauthorized' }), {
status: 401,
headers: { 'Content-Type': 'application/json' },
});
}
const token = authHeader.slice(7);
try {
const [, payloadBase64] = token.split('.');
const payload = JSON.parse(Buffer.from(payloadBase64!, 'base64').toString());
ctx.store['userId'] = payload.sub;
} catch {
return new Response(JSON.stringify({ message: 'Invalid token' }), {
status: 401,
headers: { 'Content-Type': 'application/json' },
});
}
return undefined;
};
// Create app
const app = new Elysia()
.use(cors())
.onError(({ error }) => {
console.error('Error:', error);
return onionErrorHandler({ error });
})
.get('/health', () => ({ status: 'ok' }));
// Register routes with auth
registerElysiaRoutes(app, routes, {
middlewares: [authMiddleware],
contextExtractor: (ctx: ElysiaContext) => ({
userId: ctx.store['userId'] as string,
}),
});
// Start server
app.listen(3000, () => {
console.log('Server running on http://localhost:3000');
});