Skip to content

API Reference

Complete API reference for Veloce-TS framework.


The main application class. Veloce is a named export alias of VeloceTS (same class).

new VeloceTS(config?: VeloceTSConfig)
// same as: new Veloce(config?: VeloceTSConfig)

Parameters:

  • config (optional): Configuration object

VeloceTSConfig interface:

interface VeloceTSConfig {
adapter?: 'hono' | 'express' | 'native';
title?: string;
version?: string;
description?: string;
/**
* Mount OpenAPI docs. `true` (the default) serves the spec at `/openapi.json`
* and Swagger UI at `/docs`. Pass an object to change those paths, or `false`
* to disable. Ignored when an `openapi` plugin is registered manually.
* Before 3.0.0 this option was accepted and ignored.
*/
docs?: boolean | { path?: string; openapi?: string };
cors?: CorsOptions | boolean;
/** Plugins registered at construction time (same as calling `usePlugin`). */
plugins?: Plugin[];
/**
* Event bus that `@On` listeners subscribe to. Defaults to `globalEvents`.
* Pass your own to keep a test suite from sharing subscriptions across apps.
* Added in 3.3.0.
*/
eventBus?: EventBus;
/**
* Maximum request body size in bytes. Bodies above it are rejected with 413
* before any handler runs. Defaults to 1 MiB; `0` disables the limit.
* Added in 3.0.0 — raise it if you accept file uploads.
*/
bodyLimit?: number;
/**
* Error JSON shape:
* - `rfc9457` — `application/problem+json` (default since v0.4.3)
* - `legacy` — `{ error, statusCode, details? }`
*/
errorResponseFormat?: 'rfc9457' | 'legacy';
}

Example:

const app = new Veloce({
title: 'My API',
version: '1.0.0',
description: 'API for my application',
docs: true,
cors: true,
});

Register a controller class with the application.

@Controller('/users')
class UserController {
@Get('/')
async list() {
return [];
}
}
app.include(UserController);
get(path: string, config: RouteConfig): void
Section titled “get(path: string, config: RouteConfig): void”

Register a GET route using functional API.

app.get('/users', {
handler: async (c) => {
return [{ id: 1, name: 'John' }];
},
});
post(path: string, config: RouteConfig): void
Section titled “post(path: string, config: RouteConfig): void”

Register a POST route using functional API.

const UserSchema = z.object({
name: z.string(),
email: z.string().email(),
});
app.post('/users', {
schema: { body: UserSchema },
handler: async (c) => {
const body = await c.req.json();
return { id: 2, ...body };
},
});
put(path: string, config: RouteConfig): void
Section titled “put(path: string, config: RouteConfig): void”

Register a PUT route using functional API.

delete(path: string, config: RouteConfig): void
Section titled “delete(path: string, config: RouteConfig): void”

Register a DELETE route using functional API.

patch(path: string, config: RouteConfig): void
Section titled “patch(path: string, config: RouteConfig): void”

Register a PATCH route using functional API.

Create a route builder for chaining multiple methods on the same path.

app.route('/users')
.get({
handler: async (c) => {
return [];
},
})
.post({
handler: async (c) => {
const body = await c.req.json();
return body;
},
});
group(prefix: string, callback: () => void): void
Section titled “group(prefix: string, callback: () => void): void”

Group routes under a common prefix.

app.group('/api', () => {
app.get('/users', { handler: async (c) => [] });
app.get('/posts', { handler: async (c) => [] });
});
// Creates: /api/users and /api/posts

Register global middleware.

app.use(async (c, next) => {
console.log(`${c.req.method} ${c.req.url}`);
await next();
});

Enable CORS with optional configuration.

app.useCors({
origin: 'https://example.com',
methods: ['GET', 'POST'],
credentials: true,
});
useRateLimit(options: RateLimitOptions): void
Section titled “useRateLimit(options: RateLimitOptions): void”

Enable rate limiting.

app.useRateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // 100 requests per window
});

Options:

interface RateLimitOptions {
windowMs?: number; // Window length in ms (default 60000)
max?: number; // Requests allowed per window (default 100)
keyGenerator?: (c: Context) => string;
/**
* Trust X-Forwarded-For / X-Real-IP for client identity. Leave false (the
* default) unless you run behind a trusted proxy — these headers are
* client-supplied and trivially spoofable otherwise.
*/
trustProxy?: boolean;
/**
* Counter backend. Defaults to an in-memory, per-process store, which means
* each replica enforces its own limit. Use `RedisRateLimitStore` to share one
* counter across instances. Added in 3.0.0.
*/
store?: RateLimitStore;
}
import { RedisRateLimitStore } from 'veloce-ts/middleware';
app.useRateLimit({
max: 100,
store: new RedisRateLimitStore(redis),
});
useCompression(options?: CompressionOptions): void
Section titled “useCompression(options?: CompressionOptions): void”

Enable response compression.

app.useCompression({
threshold: 1024, // Only compress responses > 1KB
});

Register a plugin with the application.

import { OpenAPIPlugin } from 'veloce-ts';
app.usePlugin(new OpenAPIPlugin({
path: '/openapi.json',
docsPath: '/docs',
}));

Register a custom error handler.

app.onError((error, c) => {
console.error('Error:', error);
return c.json({
error: error.message,
timestamp: new Date().toISOString(),
}, 500);
});
listen(port: number, callback?: () => void): Promise<ServerInstance>
Section titled “listen(port: number, callback?: () => void): Promise<ServerInstance>”

Start the server on the specified port.

app.listen(3000, () => {
console.log('Server running on http://localhost:3000');
});

Compile all routes and metadata. Called automatically by listen().

app.compile();

Get the underlying Hono instance.

const hono = app.getHono();

Get the metadata registry.

const metadata = app.getMetadata();
const routes = metadata.getRoutes();

Get the dependency injection container.

const container = app.getContainer();
container.register(DatabaseService, { scope: 'singleton' });

:::note Renamed in 3.0.0 The response builder is now exported as VeloceResponse (with Res as a short alias). The old Response export shadowed the global Web API Response in any file that imported it, so new Response(body, init) silently resolved to the builder instead. Response is still exported as a deprecated alias.

import { VeloceResponse } from 'veloce-ts'; // or: import { Res } from 'veloce-ts'
VeloceResponse.json({ ok: true }, 201);
VeloceResponse.fileFrom('./uploads', filename, { download: true }); // path-traversal safe

The individual classes below (JSONResponse, HTMLResponse, …) keep their names. :::

Response builder classes for different content types.

import { JSONResponse } from 'veloce-ts';
@Get('/data')
async getData() {
return new JSONResponse({ data: 'value' }, 200);
}
import { HTMLResponse } from 'veloce-ts';
@Get('/page')
async getPage() {
return new HTMLResponse('<h1>Hello</h1>', 200);
}
import { FileResponse } from 'veloce-ts';
@Get('/download')
async download() {
return new FileResponse('/path/to/file.pdf', {
filename: 'document.pdf',
contentType: 'application/pdf',
});
}
import { StreamResponse } from 'veloce-ts';
@Get('/stream')
async stream() {
const stream = createReadStream('/path/to/large-file.txt');
return new StreamResponse(stream, {
contentType: 'text/plain',
});
}
import { RedirectResponse } from 'veloce-ts';
@Get('/old-path')
async redirect() {
return new RedirectResponse('/new-path', 301);
}

Marks a class as a controller and optionally sets a path prefix.

Parameters:

  • prefix (optional): Path prefix for all routes in the controller

Example:

@Controller('/api/users')
class UserController {
@Get('/')
async list() {
return [];
}
}

Marks a class as a WebSocket handler.

Parameters:

  • path: WebSocket endpoint path

Example:

@WebSocket('/chat')
class ChatHandler {
@OnConnect()
handleConnect(client: WebSocketConnection) {
console.log('Connected');
}
}

Marks a class as a GraphQL resolver.

Example:

@Resolver()
class UserResolver {
@GQLQuery()
async users() {
return [];
}
}

@UseMiddleware(...middleware: Middleware[])

Section titled “@UseMiddleware(...middleware: Middleware[])”

Apply middleware to a controller or route.

Parameters:

  • middleware: One or more middleware functions

Example:

const authMiddleware = async (c, next) => {
// Auth logic
await next();
};
@Controller('/admin')
@UseMiddleware(authMiddleware)
class AdminController {
// All routes protected
}

Register a GET route.

Parameters:

  • path (optional): Route path

Example:

@Get('/users')
async getUsers() {
return [];
}
@Get('/users/:id')
async getUser(@Param('id') id: string) {
return { id };
}

Register a POST route.

Example:

@Post('/users')
async createUser(@Body(UserSchema) user: User) {
return user;
}

Register a PUT route.

Example:

@Put('/users/:id')
async updateUser(
@Param('id') id: string,
@Body(UserSchema) user: User
) {
return { id, ...user };
}

Register a DELETE route.

Example:

@Delete('/users/:id')
async deleteUser(@Param('id') id: string) {
return { success: true };
}

Register a PATCH route.

Example:

@Patch('/users/:id')
async patchUser(
@Param('id') id: string,
@Body(PartialUserSchema) data: Partial<User>
) {
return { id, ...data };
}

Register a route that responds to all HTTP methods.

Example:

@All('/webhook')
async webhook() {
return { received: true };
}

Sets the short description for the route in Swagger UI.

Sets the long description for the route operation detail panel.

Assigns a single OpenAPI tag to the route. Stackable.

Assigns multiple tags in a single decorator.

Marks the route as deprecated in the OpenAPI spec.

Example:

@Get('/products')
@Summary('List all products')
@Tags('Products', 'Catalog')
async listProducts() {
return products;
}
@Delete('/products/:id')
@Deprecated()
@Summary('Delete a product (deprecated)')
async deleteProduct(@Param('id') id: string) {
return { success: true };
}

Overrides the HTTP response status code and documents it in the OpenAPI spec.

Example:

@Post('/products')
@HttpCode(201)
async createProduct(@Body(CreateProductSchema) data: any) {
return await db.create(data);
}
@ResponseSchema(schema: ZodSchema, statusCode?: number)
Section titled “@ResponseSchema(schema: ZodSchema, statusCode?: number)”

Validates the handler response against a Zod schema and documents the response model in OpenAPI.

Example:

@Get('/products/:id')
@ResponseSchema(ProductSchema)
async getProduct(@Param('id') id: string) {
return await db.findById(id);
}

Aborts the request with 408 Request Timeout if the handler exceeds the time limit. Emits the X-Timeout-Ms header.

Parameters:

  • ms: Timeout in milliseconds
  • message (optional): Custom timeout message

Example:

@Get('/report')
@Timeout(5000)
async generateReport() {
return await heavyReport();
}

Applies rate-limiting to an individual route. Standard X-RateLimit-* headers are sent automatically.

Example:

@Post('/auth/login')
@RateLimit({ windowMs: 15 * 60_000, max: 10 })
async login(@Body(LoginSchema) credentials: any) {
return await authService.login(credentials);
}

Define a GraphQL query.

:::caution Not @Query() Query at the package root is the HTTP query-parameter decorator. A resolver method decorated with it never becomes a GraphQL field. The GraphQL decorators are @GQLQuery, @GQLMutation and @GQLSubscription — Mutation and Subscription are also exported as aliases, but Query is not, precisely because the name is taken. :::

Example:

@Resolver()
class UserResolver {
@GQLQuery()
async users() {
return await db.getUsers();
}
@GQLQuery()
async user(@Arg('id', z.number()) id: number) {
return await db.getUserById(id);
}
}

Define a GraphQL mutation. Also exported as Mutation.

Example:

@GQLMutation()
async createUser(@Arg('input', CreateUserInput) input: any) {
return await db.createUser(input);
}

Define a GraphQL subscription. Also exported as Subscription.

Example:

@GQLSubscription()
async userCreated() {
return pubsub.asyncIterator('USER_CREATED');
}

Handle WebSocket connection events.

Example:

@OnConnect()
handleConnect(client: WebSocketConnection) {
console.log(`Client ${client.id} connected`);
}

Handle WebSocket message events with optional validation.

Parameters:

  • schema (optional): Zod schema for message validation

Example:

const MessageSchema = z.object({
type: z.string(),
content: z.string(),
});
@OnMessage(MessageSchema)
handleMessage(
client: WebSocketConnection,
message: z.infer<typeof MessageSchema>
) {
client.broadcast(message);
}

Handle WebSocket disconnection events.

Example:

@OnDisconnect()
handleDisconnect(client: WebSocketConnection) {
console.log(`Client ${client.id} disconnected`);
}

Extract and validate request body.

Parameters:

  • schema (optional): Zod schema for validation

Example:

const UserSchema = z.object({
name: z.string().min(2),
email: z.string().email(),
});
@Post('/users')
async createUser(@Body(UserSchema) user: z.infer<typeof UserSchema>) {
return user;
}

Extract and validate query parameters.

Parameters:

  • schema (optional): Zod schema for validation

Example:

const SearchSchema = z.object({
q: z.string(),
page: z.string().transform(Number).default('1'),
});
@Get('/search')
async search(@Query(SearchSchema) query: z.infer<typeof SearchSchema>) {
return { query: query.q, page: query.page };
}

Extract route parameters.

Parameters:

  • name (optional): Specific parameter name to extract

Example:

@Get('/users/:id')
async getUser(@Param('id') id: string) {
return { id };
}
// Extract all params
@Get('/users/:userId/posts/:postId')
async getPost(@Param() params: { userId: string; postId: string }) {
return params;
}

@Header(name?: string, schema?: ZodSchema)

Section titled “@Header(name?: string, schema?: ZodSchema)”

Extract and validate headers.

Parameters:

  • name (optional): Specific header name to extract
  • schema (optional): Zod schema for validation

Example:

@Get('/protected')
async getProtected(@Header('authorization') auth: string) {
return { auth };
}
const HeaderSchema = z.object({
'x-api-key': z.string(),
});
@Get('/api')
async apiEndpoint(@Header(HeaderSchema) headers: z.infer<typeof HeaderSchema>) {
return headers;
}

@Cookie(name?: string, schema?: ZodSchema)

Section titled “@Cookie(name?: string, schema?: ZodSchema)”

Extract and validate cookies.

Parameters:

  • name (optional): Specific cookie name to extract
  • schema (optional): Zod schema for validation

Example:

@Get('/profile')
async getProfile(@Cookie('session') sessionId: string) {
return { sessionId };
}

Inject the Hono Context. For the Web standard Request, use c.req.raw.

import type { Context } from 'hono';
@Post('/echo')
async echo(@Ctx() c: Context) {
return await c.req.json();
}

Inject Hono’s request object (c.req). Exported from veloce-ts. For Request (Fetch API), prefer @Ctx() and c.req.raw.

@Get('/path')
path(@Req() req: import('hono').HonoRequest) {
return { path: req.path };
}

@Depends(provider: Provider, scope?: Scope)

Section titled “@Depends(provider: Provider, scope?: Scope)”

Inject dependencies.

Parameters:

  • provider: Service class to inject
  • scope (optional): Dependency scope (‘singleton’, ‘request’, ‘transient’)

Example:

class UserService {
async getUsers() {
return [];
}
}
@Controller('/users')
class UserController {
@Get('/')
async list(@Depends(UserService) userService: UserService) {
return await userService.getUsers();
}
}

GraphQL argument with validation.

Parameters:

  • name: Argument name
  • schema (optional): Zod schema for validation

Example:

@Resolver()
class UserResolver {
@GQLQuery()
async user(@Arg('id', z.number().int().positive()) id: number) {
return await db.getUserById(id);
}
}

interface Plugin {
name: string;
version?: string;
dependencies?: string[];
install(app: VeloceTS): void | Promise<void>;
}
class MyPlugin implements Plugin {
name = 'my-plugin';
version = '1.0.0';
install(app: VeloceTS) {
// Add routes
app.get('/plugin-route', {
handler: async (c) => {
return { message: 'From plugin' };
},
});
// Add middleware
app.use(async (c, next) => {
console.log('Plugin middleware');
await next();
});
// Register services
app.getContainer().register(MyService, { scope: 'singleton' });
}
}
const app = new VeloceTS();
app.usePlugin(new MyPlugin());
import { OpenAPIPlugin } from 'veloce-ts';
app.usePlugin(new OpenAPIPlugin({
path: '/openapi.json',
docsPath: '/docs',
title: 'My API',
version: '1.0.0',
description: 'API description',
}));

Options:

  • path: OpenAPI spec endpoint (default: /openapi.json)
  • docsPath: Swagger UI endpoint (default: /docs)
  • title: API title
  • version: API version
  • description: API description
import { GraphQLPlugin } from 'veloce-ts';
app.usePlugin(new GraphQLPlugin({
path: '/graphql',
playground: true,
introspection: true,
}));

Options:

  • path: GraphQL endpoint (default: /graphql)
  • playground: Enable GraphQL Playground (default: true)
  • introspection: Enable introspection (default: true)
import { WebSocketPlugin } from 'veloce-ts';
app.usePlugin(new WebSocketPlugin({
path: '/ws',
maxConnections: 1000,
}));

Options:

  • path: WebSocket endpoint (default: /ws)
  • maxConnections: Maximum concurrent connections

Connect Drizzle (or any other ORM) to the dependency injection container.

Registers a database instance as a singleton in the DIContainer.

Parameters:

  • app: VeloceTS application instance
  • db: Database instance (Drizzle or other ORM)
  • token (optional): Custom injection token (defaults to DB_TOKEN)

Example:

import { registerDrizzle } from 'veloce-ts';
import { drizzle } from 'drizzle-orm/bun-sqlite';
const db = drizzle(new Database('my.db'));
registerDrizzle(app, db);

Parameter decorator that injects the registered database instance. Alias of @Depends(DB_TOKEN).

Parameters:

  • token (optional): Custom injection token (defaults to DB_TOKEN)

Example:

import { InjectDB } from 'veloce-ts';
@Controller('/products')
class ProductController {
@Get('/')
async list(@InjectDB() db: typeof drizzleDb) {
return await db.select().from(products);
}
@Post('/')
@HttpCode(201)
async create(
@Body(CreateProductSchema) data: any,
@InjectDB() db: typeof drizzleDb
) {
return await db.insert(products).values(data).returning();
}
}

Default symbol used as the injection token for the database instance.

import { DB_TOKEN } from 'veloce-ts';
// Custom token example
const CUSTOM_DB_TOKEN = Symbol('CUSTOM_DB');
registerDrizzle(app, db, CUSTOM_DB_TOKEN);
@Get('/')
async list(@InjectDB(CUSTOM_DB_TOKEN) db: any) {
return await db.select().from(table);
}

createTestApp(config?: VeloceTSConfig): VeloceTS

Section titled “createTestApp(config?: VeloceTSConfig): VeloceTS”

Creates a test instance of the application.

Example:

import { createTestApp } from 'veloce-ts/testing';
const app = createTestApp();
app.include(UserController);

HTTP client for making test requests with a fluent assertion API.

Request Methods:

  • get(path: string, options?: RequestOptions): Promise<TestResponse>
  • post(path: string, body?: any, options?: RequestOptions): Promise<TestResponse>
  • put(path: string, body?: any, options?: RequestOptions): Promise<TestResponse>
  • delete(path: string, options?: RequestOptions): Promise<TestResponse>
  • patch(path: string, body?: any, options?: RequestOptions): Promise<TestResponse>

Auth Helpers:

  • withToken(token: string): TestClient — creates an immutable instance with Authorization: Bearer <token> pre-set
  • withHeaders(headers: Record<string, string>): TestClient — creates an immutable instance with additional headers
  • loginAs(credentials, endpoint?): Promise<TestClient> — logs in, extracts the JWT, and returns an authenticated client
  • registerAndLogin(user, endpoints?): Promise<TestClient> — registers and logs in in a single call
  • clearAuth(): void — clears the stored token

Response wrapper with chainable assertion methods returned by TestClient requests.

Properties:

  • status: number — HTTP status code
  • ok: boolean — true if status is 2xx
  • headers: Headers — response headers
  • body: any — parsed JSON body
  • text: string — raw response text

Assertion Methods:

  • expectStatus(code: number): TestResponse
  • expectOk(): TestResponse — asserts 200
  • expectCreated(): TestResponse — asserts 201
  • expectNotFound(): TestResponse — asserts 404
  • expectUnauthorized(): TestResponse — asserts 401
  • expectForbidden(): TestResponse — asserts 403
  • expectBadRequest(): TestResponse — asserts 400
  • expectJson(partialObject: any): TestResponse — partial body check
  • expectField(field: string, value?: any): TestResponse — verify a specific field
  • expectHeader(name: string, value?: string): TestResponse — verify a response header
  • expectArrayLength(n: number): TestResponse — verify array length in response

Example:

import { TestClient } from 'veloce-ts/testing';
const client = new TestClient(app);
// Fluent assertions
const response = await client.get('/users');
await response.expectOk().expectArrayLength(2);
// Auth helpers
const authClient = await client.loginAs({
email: 'user@example.com',
password: 'password123'
});
await authClient.get('/profile').then(r => r.expectOk());
// Chained assertions
const res = await client.post('/products', {
name: 'Product A',
price: 29.99
});
await res.expectCreated()
.expectField('id')
.expectJson({ name: 'Product A' });
// Manual assertions (still supported)
const raw = await client.get('/users');
expect(raw.status).toBe(200);
const data = raw.body;
expect(data).toBeArray();

mockDependency(provider: Class, mock: any): void

Section titled “mockDependency(provider: Class, mock: any): void”

Mocks a dependency in the DI container.

Example:

import { mockDependency } from 'veloce-ts/testing';
const mockUserService = {
getUsers: async () => [{ id: 1, name: 'Test' }],
};
mockDependency(UserService, mockUserService);

Base exception class for HTTP errors.

Constructor:

new HTTPException(status: number, message: string, details?: any)

Example:

import { HTTPException } from 'veloce-ts';
@Get('/users/:id')
async getUser(@Param('id') id: string) {
const user = await db.getUserById(id);
if (!user) {
throw new HTTPException(404, 'User not found');
}
return user;
}

Exception for validation errors (422 status).

Example:

import { ValidationException } from 'veloce-ts';
throw new ValidationException([
{ path: 'email', message: 'Invalid email format' },
]);

Exception for not found errors (404 status).

Example:

import { NotFoundException } from 'veloce-ts';
throw new NotFoundException('Resource not found');

Exception for unauthorized errors (401 status).

Example:

import { UnauthorizedException } from 'veloce-ts';
throw new UnauthorizedException('Invalid credentials');

Exception for forbidden errors (403 status).

Example:

import { ForbiddenException } from 'veloce-ts';
throw new ForbiddenException('Access denied');

Exception for conflict errors (409 status).

Example:

import { ConflictException } from 'veloce-ts';
throw new ConflictException('Email already registered');

Exception for gone errors (410 status). Use when a resource has been permanently deleted.

Example:

import { GoneException } from 'veloce-ts';
throw new GoneException('This resource no longer exists');

Exception for payload too large errors (413 status).

Example:

import { PayloadTooLargeException } from 'veloce-ts';
throw new PayloadTooLargeException('File exceeds the 10MB limit');

Exception for unprocessable entity errors (422 status). Use for semantic validation failures.

Example:

import { UnprocessableEntityException } from 'veloce-ts';
throw new UnprocessableEntityException('Age must be at least 18');

Exception for rate limit exceeded errors (429 status).

Example:

import { TooManyRequestsException } from 'veloce-ts';
throw new TooManyRequestsException('Too many requests. Try again in 1 minute.');

Exception for service unavailable errors (503 status).

Example:

import { ServiceUnavailableException } from 'veloce-ts';
throw new ServiceUnavailableException('Database is temporarily unavailable');

interface VeloceTSConfig {
adapter?: 'hono' | 'express' | 'native';
title?: string;
version?: string;
description?: string;
docs?: boolean | { path?: string; openapi?: string };
cors?: CorsOptions | boolean;
errorResponseFormat?: 'rfc9457' | 'legacy';
}

Register plugins with app.usePlugin(new SomePlugin()) — there is no plugins: [] field on this config type.

interface RouteConfig {
handler: (c: Context) => any | Promise<any>;
schema?: {
body?: ZodSchema;
query?: ZodSchema;
params?: ZodSchema;
headers?: ZodSchema;
};
middleware?: Middleware[];
}
type Middleware = (c: Context, next: () => Promise<void>) => Promise<void> | void;

Hono context object with request and response utilities.

Properties:

  • req: Request object
  • res: Response object
  • env: Environment variables
  • executionCtx: Execution context

Methods:

  • json(data: any, status?: number): Response
  • text(text: string, status?: number): Response
  • html(html: string, status?: number): Response
  • redirect(url: string, status?: number): Response
type Scope = 'singleton' | 'request' | 'transient';
interface WebSocketConnection {
id: string;
send(data: any): void;
broadcast(data: any, includeSelf?: boolean): void;
join(room: string): void;
leave(room: string): void;
close(): void;
}

Quick reference for the APIs added or changed in v2.0.0. Breaking changes are marked.

APINotes
await app.getFetchHandler()Returns a fetch(request, env, ctx) handler. The supported Cloudflare Workers deploy path — listen() throws on workerd. Auto-compiles routes on first call. Also usable with Bun.serve({ fetch }) / Deno.serve().
await app.shutdown()Now also runs registered onShutdown() handlers and plugin onStop() hooks (it previously closed only the server).
// worker.ts — Cloudflare Workers
const app = new Veloce({ title: 'My API' });
app.get('/hello', { handler: () => ({ message: 'hi' }) });
export default { fetch: await app.getFetchHandler() };

Typed functional routes — RouteConfig<S>

Section titled “Typed functional routes — RouteConfig<S>”

app.get/post/put/delete/patch and app.route() are generic over the declared schema bag, so handler arguments are inferred. Injection order is body, query, params, headers; only declared schemas contribute an argument.

app.post('/users', {
schema: { body: CreateUserSchema, query: QuerySchema },
handler: async (c, body, query) => body.name, // both fully typed
});

Supporting types: RouteSchemas, SchemaArgs<S>.

APINotes
@Controller(prefix, { scope })'singleton' (new default) | 'request' | 'transient'.
APINotes
JWTProvider verification/revocation methodsBreaking — now async: verifyAccessToken, verifyRefreshToken, refreshAccessToken, blacklistToken, isBlacklisted, cleanupBlacklist.
JWTConfig.blacklistA TokenBlacklist implementation. Defaults to MemoryTokenBlacklist.
MemoryTokenBlacklist / RedisTokenBlacklistExported from veloce-ts/auth. Use the Redis store to propagate revocation across replicas.
JWTConfig.privateKey / publicKeyPEM keys, required for RS256/RS384/RS512 (previously those algorithms silently used the shared secret).
RateLimitOptions.trustProxyfalse by default. When unset, X-Forwarded-For/X-Real-IP are ignored because they are client-supplied.
APINotes
Plugin.onStart?(app)Runs after the server starts listening.
Plugin.onStop?(app)Runs during graceful shutdown, reverse install order. Errors are collected into an AggregateError.
APINotes
CacheManager.destroy()Releases every registered store’s timers and connections.
CacheManager.reset()Now destroys stores before replacing them (it previously leaked their cleanup intervals).
APINotes
createTimeoutMiddleware(ms, message?)Exported from veloce-ts/middleware. Shared by @Timeout() and the functional timeout option.

WebSocketPlugin options: heartbeatIntervalMs (default 30000, 0 disables), heartbeatTimeoutMs (default 2 × interval), idleTimeoutMs (default 0, disabled), maxMessageSizeBytes (default 1048576). See WebSockets.

APINotes
@Returns(typeOrSchema, options?)From veloce-ts/graphql. Declares a field’s return type; options are { name?, nullable?, list? }. Resolvers and object types did not work before v2.0.0.
APINotes
NotImplementedErrorThrown by base BaseRepository.count() / updateMany() / deleteMany(), which previously did silent full table scans. Bundled adapters override all three.