Skip to content

@trokky/routes

@trokky/routes provides framework-agnostic HTTP handlers that can be adapted to any web framework. It defines the REST API structure and handles request/response transformation.

Terminal window
npm install @trokky/routes

Note: This package is typically used through framework integrations like @trokky/express rather than directly.

Routes are defined as pure functions that take an HttpRequest and return an HttpResponse:

type RouteHandler = (
request: HttpRequest,
context: RouteContext
) => Promise<HttpResponse>;

Framework adapters convert between framework-specific request/response objects and this standard format.

interface HttpRequest {
method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
path: string;
params: Record<string, string>;
query: Record<string, string>;
body?: any;
headers: Record<string, string>;
user?: User;
}
interface HttpResponse {
status: number;
headers?: Record<string, string>;
body?: any;
}
MethodPathDescription
GET/api/documents/:typeList documents
GET/api/documents/:type/:idGet document
POST/api/documents/:typeCreate document
PUT/api/documents/:type/:idUpdate document
PATCH/api/documents/:type/:idPartial update
DELETE/api/documents/:type/:idDelete document
MethodPathDescription
GET/api/mediaList media
GET/api/media/:idGet media
POST/api/mediaUpload media
PATCH/api/media/:idUpdate metadata
DELETE/api/media/:idDelete media
MethodPathDescription
POST/api/auth/loginUser login
POST/api/auth/logoutUser logout
POST/api/auth/refreshRefresh token
GET/api/auth/meCurrent user
MethodPathDescription
GET/api/config/schemasGet schemas
GET/api/config/studioStudio config
GET/api/config/structureNavigation structure

For custom integrations, you can use route handlers directly:

import { createRouteHandlers, RouteContext } from '@trokky/routes';
import { TrokkyCore } from '@trokky/core';
const core = new TrokkyCore({ /* config */ });
const handlers = createRouteHandlers(core);
// Use handlers in your framework
const response = await handlers.documents.list({
method: 'GET',
path: '/api/documents/post',
params: { type: 'post' },
query: { limit: '10' },
headers: {},
});
// response = { status: 200, body: { data: [...] } }
interface DocumentHandlers {
list(request: HttpRequest): Promise<HttpResponse>;
get(request: HttpRequest): Promise<HttpResponse>;
create(request: HttpRequest): Promise<HttpResponse>;
update(request: HttpRequest): Promise<HttpResponse>;
patch(request: HttpRequest): Promise<HttpResponse>;
delete(request: HttpRequest): Promise<HttpResponse>;
}
interface MediaHandlers {
list(request: HttpRequest): Promise<HttpResponse>;
get(request: HttpRequest): Promise<HttpResponse>;
upload(request: HttpRequest): Promise<HttpResponse>;
update(request: HttpRequest): Promise<HttpResponse>;
delete(request: HttpRequest): Promise<HttpResponse>;
}
interface AuthHandlers {
login(request: HttpRequest): Promise<HttpResponse>;
logout(request: HttpRequest): Promise<HttpResponse>;
refresh(request: HttpRequest): Promise<HttpResponse>;
me(request: HttpRequest): Promise<HttpResponse>;
}
GET /api/documents/post?limit=10&offset=0&orderBy=createdAt&order=desc
ParameterTypeDefaultDescription
limitnumber20Items per page
offsetnumber0Skip items
orderBystring_createdAtSort field
orderstringdescSort direction
expandstring-Expand references
GET /api/documents/post?filter[status]=published&filter[author]=author-123
GET /api/documents/post?expand=author,categories

Returns documents with expanded references:

{
"_id": "post-123",
"title": "My Post",
"author": {
"_id": "author-456",
"name": "John Doe"
}
}
{
"data": { ... },
"meta": {
"total": 100,
"limit": 10,
"offset": 0
}
}
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Validation failed",
"details": [
{ "field": "title", "message": "Required field" }
]
}
}
import { authMiddleware } from '@trokky/routes';
const middleware = authMiddleware({
jwtSecret: process.env.JWT_SECRET,
exclude: ['/api/auth/login', '/api/documents/*:GET'],
});
import { rateLimitMiddleware } from '@trokky/routes';
const middleware = rateLimitMiddleware({
windowMs: 15 * 60 * 1000,
max: 100,
});

To create an adapter for a new framework:

import { createRouteHandlers, HttpRequest, HttpResponse } from '@trokky/routes';
// Convert framework request to HttpRequest
function toHttpRequest(frameworkRequest: any): HttpRequest {
return {
method: frameworkRequest.method,
path: frameworkRequest.url,
params: frameworkRequest.params,
query: frameworkRequest.query,
body: frameworkRequest.body,
headers: frameworkRequest.headers,
user: frameworkRequest.user,
};
}
// Convert HttpResponse to framework response
function toFrameworkResponse(response: HttpResponse, frameworkRes: any) {
frameworkRes.status(response.status);
if (response.headers) {
Object.entries(response.headers).forEach(([key, value]) => {
frameworkRes.setHeader(key, value);
});
}
frameworkRes.json(response.body);
}
// Create handler
export function createHandler(core: TrokkyCore) {
const handlers = createRouteHandlers(core);
return async (frameworkReq: any, frameworkRes: any) => {
const request = toHttpRequest(frameworkReq);
const response = await handlers.route(request);
toFrameworkResponse(response, frameworkRes);
};
}

All inputs are validated against schemas:

  • Path traversal protection
  • SQL injection prevention
  • XSS sanitization
  • Size limits

Protected routes require valid JWT:

// Public routes (no auth required)
GET /api/documents/*
GET /api/media/*
POST /api/auth/login
// Protected routes (auth required)
POST /api/documents/*
PUT /api/documents/*
DELETE /api/documents/*
POST /api/media