Express Adapter
Express Adapter
Section titled “Express Adapter”The Veloce-TS Express adapter lets you mount a Veloce-TS application inside an existing Express.js app — or use Express as the HTTP transport layer underneath Veloce-TS. This is useful when migrating from Express to Veloce-TS incrementally, or when you need Express-specific middleware (e.g. Passport.js, multer, session).
:::note v0.4.0 Rewrite The Express adapter was completely rewritten in v0.4.0 for full ESM compatibility, correct body handling, and proper error delegation. If you were using an older version, review the migration notes below. :::
Table of Contents
Section titled “Table of Contents”- Installation
- Basic Usage
- Adding Express Middleware Before the Bridge
- Mounting on a Sub-Path
- Error Handling
- Migration from Previous Versions
- When to Use the Express Adapter
Installation
Section titled “Installation”Express is a peer dependency — install it alongside Veloce-TS:
# Using Bunbun add veloce-ts expressbun add -d @types/express
# Using npmnpm install veloce-ts expressnpm install --save-dev @types/expressBasic Usage
Section titled “Basic Usage”import express from 'express';import { VeloceTS, Controller, Get, Post, Body } from 'veloce-ts';import { ExpressAdapter } from 'veloce-ts/adapters/express';import { z } from 'zod';
// 1. Define your Veloce-TS app as usualconst UserSchema = z.object({ name: z.string().min(2), email: z.string().email(),});
@Controller('/users')class UserController { @Get('/') async list() { return [{ id: 1, name: 'John' }]; }
@Post('/') async create(@Body(UserSchema) user: z.infer<typeof UserSchema>) { return { id: 2, ...user }; }}
const veloce = new VeloceTS({ title: 'My API', docs: true });veloce.include(UserController);await veloce.compile();
// 2. Create an Express app and mount Veloce-TSconst app = express();
// Parse JSON bodies for Express routesapp.use(express.json());
// Mount the Veloce-TS adapter — it handles all requests under /apiconst adapter = new ExpressAdapter(veloce);app.use('/api', adapter.getRouter());
// You can still have regular Express routes alongsideapp.get('/health', (req, res) => { res.json({ status: 'ok', framework: 'express+veloce' });});
app.listen(3000, () => { console.log('Server running on http://localhost:3000'); console.log('Veloce-TS routes available at http://localhost:3000/api'); console.log('Swagger UI at http://localhost:3000/api/docs');});Adding Express Middleware Before the Bridge
Section titled “Adding Express Middleware Before the Bridge”The v0.4.0 adapter accepts a pre-created Express app as the second argument to the constructor. This lets you add middleware (auth, rate limiting, multipart, etc.) that runs before requests reach Veloce-TS:
import express from 'express';import passport from 'passport';import multer from 'multer';import { ExpressAdapter } from 'veloce-ts/adapters/express';
const expressApp = express();
// Add any Express middleware you needexpressApp.use(express.json());expressApp.use(express.urlencoded({ extended: true }));expressApp.use(passport.initialize());
const upload = multer({ dest: 'uploads/' });
// Create the adapter, passing the pre-configured Express appconst adapter = new ExpressAdapter(veloce, expressApp);
// Now mount to your main app or listen directlyexpressApp.use('/api', adapter.getRouter());expressApp.listen(3000);Mounting on a Sub-Path
Section titled “Mounting on a Sub-Path”Mount Veloce-TS on any sub-path of your Express app:
const mainApp = express();mainApp.use(express.json());
// v1 API powered by Veloce-TSconst v1Adapter = new ExpressAdapter(veloceV1App);mainApp.use('/api/v1', v1Adapter.getRouter());
// v2 API powered by another Veloce-TS appconst v2Adapter = new ExpressAdapter(veloceV2App);mainApp.use('/api/v2', v2Adapter.getRouter());
// Legacy routes still on ExpressmainApp.use('/legacy', legacyRouter);
mainApp.listen(3000);Error Handling
Section titled “Error Handling”The v0.4.0 adapter correctly delegates unexpected errors to the Express error pipeline via next(err), so your custom Express error handlers are called:
import express, { Request, Response, NextFunction } from 'express';import { ExpressAdapter } from 'veloce-ts/adapters/express';
const app = express();app.use(express.json());
const adapter = new ExpressAdapter(veloce);app.use('/api', adapter.getRouter());
// This error handler will be called for any unhandled errors,// including those thrown by Veloce-TS handlersapp.use((err: Error, req: Request, res: Response, next: NextFunction) => { console.error('Unhandled error:', err); res.status(500).json({ error: 'Internal Server Error', message: process.env.NODE_ENV === 'development' ? err.message : undefined, });});
app.listen(3000);Body Handling
Section titled “Body Handling”The v0.4.0 adapter correctly handles both raw (Buffer) and pre-parsed bodies:
- JSON/urlencoded: If Express middleware has already parsed the body (e.g. via
express.json()), the adapter uses the parsed value - Raw buffer: If the body is raw (e.g. file uploads, webhooks), it’s passed through correctly
transfer-encodingheader: The adapter omits this header when forwarding responses, which was a source of bugs in older versions
// ✓ Works correctly with body parsers activeapp.use(express.json());app.use('/api', adapter.getRouter());
// ✓ Also works without body parsers (raw body)app.use('/webhooks', adapter.getRouter());Migration from Previous Versions
Section titled “Migration from Previous Versions”If you were using the Express adapter from v0.3.x or earlier, here are the changes:
Import path
Section titled “Import path”// Before (v0.3.x)import { ExpressAdapter } from 'veloce-ts';
// After (v0.4.0)import { ExpressAdapter } from 'veloce-ts/adapters/express';Constructor signature
Section titled “Constructor signature”// Beforeconst adapter = new ExpressAdapter(veloceApp);
// After — same, but now accepts optional pre-created Express instanceconst adapter = new ExpressAdapter(veloceApp);// orconst adapter = new ExpressAdapter(veloceApp, expressApp);ESM compatibility
Section titled “ESM compatibility”The adapter no longer needs declare const require: any workarounds. It uses Function('return require')() internally for lazy Express loading, which works seamlessly in ESM projects.
When to Use the Express Adapter
Section titled “When to Use the Express Adapter”Use the Express adapter when:
| Scenario | Recommendation |
|---|---|
| Starting a new project | Use Veloce-TS standalone (Hono adapter) for best performance |
| Migrating an Express app to Veloce-TS | Use the adapter to migrate route by route |
| Need Passport.js, multer, or other Express-only middleware | Use the adapter |
| Deploying on a platform that only supports Express | Use the adapter |
| Need maximum performance | Use Veloce-TS standalone |
Next Steps
Section titled “Next Steps”- Learn about Plugins for extending Veloce-TS
- Explore Dependency Injection
- Read the Getting Started Guide