Derby.js Adapter
The Derby.js adapter connects the Derby.js runtime to Jumentix HTTP contracts. It stays at the edge: receive the request, normalize input, call use cases, and map the result back to a response.
Integrated technology
Full-stack realtime MVC framework for collaborative applications.
- Runtime model: Framework-managed Node process
- Jumentix contract: handlers call controllers/use cases without leaking framework types into the domain.
When to use
Use it for: Collaborative product surfaces that benefit from realtime MVC conventions at the edge.
When to avoid
Avoid for plain APIs where the collaboration model is unnecessary overhead.
How to start or compose
Real entrypoint:
apps/backend-template/src/interface/HTTP/adapters/derby-js/derby-js.tsapps/backend-template/src/interface/HTTP/adapters/start-rest-api.tswhen the adapter uses environment-driven bootstrap
This adapter is composed by the runtime/platform and has no dedicated dev:* script.
# This adapter is composed by its platform runtime.
# Keep controllers framework-free and wire them from the adapter entrypoint.Complete example: Task and Category at the HTTP edge
type Category = {
id: string;
name: string;
};
type Task = {
id: string;
title: string;
categoryId: string;
completed: boolean;
};
type CreateTaskRequest = {
title: string;
categoryId: string;
};
type HttpRequest = {
body: unknown;
};
type HttpResponse = {
status: number;
body: unknown;
};
const adapterProfile = {
adapter: 'derby-js',
framework: 'Derby.js',
runtime: 'Framework-managed Node process',
entrypoint: 'apps/backend-template/src/interface/HTTP/adapters/derby-js/derby-js.ts'
} as const;
class TaskCatalog {
private readonly categories = new Map<string, Category>();
private readonly tasks = new Map<string, Task>();
createCategory(name: string): Category {
const category = { id: crypto.randomUUID(), name };
this.categories.set(category.id, category);
return category;
}
createTask(input: CreateTaskRequest): Task {
if (!this.categories.has(input.categoryId)) {
throw new Error('Category not found');
}
const task = {
id: crypto.randomUUID(),
title: input.title.trim(),
categoryId: input.categoryId,
completed: false
};
this.tasks.set(task.id, task);
return task;
}
listTasksByCategory(categoryId: string): Task[] {
return [...this.tasks.values()].filter((task) => task.categoryId === categoryId);
}
}
const catalog = new TaskCatalog();
const delivery = catalog.createCategory('Delivery');
const finance = catalog.createCategory('Finance');
catalog.createTask({ title: 'Prepare invoice batch', categoryId: finance.id });
export async function createTaskController(request: HttpRequest): Promise<HttpResponse> {
const input = request.body as Partial<CreateTaskRequest>;
if (!input.title || !input.categoryId) {
return { status: 400, body: { error: 'title and categoryId are required' } };
}
try {
const task = catalog.createTask({ title: input.title, categoryId: input.categoryId });
return { status: 201, body: { adapterProfile, task } };
} catch (error) {
return {
status: 404,
body: { error: error instanceof Error ? error.message : 'Unknown error' }
};
}
}
export async function listDeliveryTasksController(): Promise<HttpResponse> {
return {
status: 200,
body: { category: delivery, tasks: catalog.listTasksByCategory(delivery.id) }
};
}Adoption checklist
- The adapter stays inside the HTTP layer.
- Controllers receive normalized data and call use cases.
TaskandCategorybelong to the domain, not the framework.- Errors are mapped to responses at the edge.