Migration Guide: v3.x to v4.x
This guide helps you migrate from YasuiJS 3.x (Express-based) to YasuiJS 4.x (Web Standards with SRVX).
Overview of Changes
YasuiJS 4.x represents a major architectural shift:
- Removed Express dependency - Now uses Web Standards
- createServer() - Uses srvx for Node.js, Deno, and Bun
- createApp() - Returns a standard fetch handler for any Web Standards platform
- Edge-ready - Deploy to Cloudflare Workers, Vercel Edge, Netlify Edge, Deno Deploy (via createApp)
- Serverless compatible - Works with AWS Lambda, Vercel Functions, Netlify Functions (via createApp)
- Breaking changes - Express middleware no longer compatible
- New features - TLS/HTTPS support, HTTP/2 on Node.js
Breaking Changes
1. Express Middleware Not Compatible
Before (v3.x):
import cors from 'cors';
import helmet from 'helmet';
yasui.createServer({
middlewares: [cors(), helmet()]
});After (v4.x): Express middleware is not compatible. You must either:
- Find Web Standards-compatible alternatives
- Write native YasuiJS middlewares
@Middleware()
export class CorsMiddleware implements IMiddleware {
async use(@Req() req: Request, @Next() next: NextFunction) {
const response = await next();
const headers = new Headers(response.headers);
headers.set('Access-Control-Allow-Origin', '*');
headers.set('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, PATCH');
headers.set('Access-Control-Allow-Headers', 'Content-Type, Authorization');
return new Response(response.body, {
status: response.status,
statusText: response.statusText,
headers,
});
}
}
yasui.createServer({
middlewares: [CorsMiddleware]
});2. Response Object No Longer Supported
@Res() is removed - no longer supported.
Before (v3.x):
@Middleware()
export class AuthMiddleware {
use(@Req() req: Request, @Res() res: Response) {
if (!req.headers.authorization) {
// Using @Res() was possible but not recommended
res.status(401).json({ error: 'Unauthorized' });
return;
}
}
}After (v4.x):
@Middleware()
export class AuthMiddleware {
use(@Req() req: Request) {
if (!req.headers.get('authorization')) {
// Throw errors or return Response objects
throw new HttpError(401, 'Unauthorized');
}
// Will continue to next middleware or controller if you return nothing/void
}
}3. Request Object Changes
@Req() provides a web-standard Request object, rather than Express; only some properties remain compatible.
Express-compatible properties (still available):
req.path- Pathname without query stringreq.hostname- Host without portreq.protocol- "http" or "https"req.ip- Client IP addressreq.query- Parsed query objectreq.cookies- Parsed cookies objectreq.body- Parsed request bodyreq.headers- Returns plain object for property access
After (v4.x):
@Get('/users')
getUsers(@Req() req: Request) {
// Headers via .get() on native Headers object
const auth = req.headers.get('authorization');
// Express-compatible properties still work
const auth = req.headers.authorization;
const page = req.query.page;
const path = req.path;
}4. Custom Response Handling Changes
Before (v3.x):
@Get('/custom')
customResponse(@Res() res: Response) {
res.status(418).json({ message: "I'm a teapot" });
}After (v4.x):
@Get('/custom')
customResponse() {
// Option 1: Return Web Standards Response
return new Response(JSON.stringify({ message: "I'm a teapot" }), {
status: 418,
headers: { 'Content-Type': 'application/json' }
});
}5. createApp() Return Type
Before (v3.x):
import express from 'express';
const app = yasui.createApp({ controllers: [UserController] });
// app is Express Application
app.use(express.json());
app.listen(3000);After (v4.x):
import { serve } from 'srvx';
const app = yasui.createApp({ controllers: [UserController] });
// app is FetchHandler { fetch: Function }
serve({
fetch: app.fetch,
port: 3000
});6. Configuration Changes
Before (v3.x):
yasui.createServer({
controllers: [UserController],
middlewares: [cors(), helmet()],
protocol: 'http',
port: 3000
});After (v4.x):
yasui.createServer({
controllers: [UserController],
middlewares: [CorsMiddleware], // Only YasuiJS middlewares
port: 3000,
tls: { // New: TLS/HTTPS support
cert: './cert.pem',
key: './key.pem'
},
runtimeOptions: { // New: Runtime-specific options
node: {
http2: true
}
}
});New options:
tls- TLS/HTTPS configurationhostname- Server hostnameruntimeOptions- Runtime-specific configuration
Deprecated:
protocol- Auto-determined bytlsconfig
Migration Steps
Step 1: Update Dependencies
npm install yasui@latest
# or
pnpm update yasuiSwagger UI Changes:
YasuiJS v4 serves Swagger UI assets from CDN by default - no additional packages needed.
If you were using swagger-ui-express in v3:
npm uninstall swagger-ui-express
# or
pnpm remove swagger-ui-expressNo code changes needed - Swagger UI works out of the box with zero configuration. The CDN approach also enables Swagger UI to work on all runtimes, including edge environments.
Step 2: Remove Express Middleware
Identify all Express middleware in your codebase:
// REMOVE these
import cors from 'cors';
import helmet from 'helmet';
import morgan from 'morgan';
yasui.createServer({
middlewares: [cors(), helmet(), morgan('dev')] // ❌ No longer works
});Step 3: Replace with Native Middlewares
Write YasuiJS middlewares for each feature:
// Create native CORS middleware
@Middleware()
export class CorsMiddleware implements IMiddleware {
async use(@Req() req: Request, @Next() next: NextFunction) {
const response = await next();
const headers = new Headers(response.headers);
headers.set('Access-Control-Allow-Origin', '*');
return new Response(response.body, {
status: response.status,
headers,
});
}
}
// Create native logging middleware
@Middleware()
export class LoggingMiddleware implements IMiddleware {
async use(@Req() req: Request, @Logger() logger: LoggerService, @Next() next: NextFunction) {
logger.log(`${req.method} ${req.path}`);
return await next();
}
}
yasui.createServer({
middlewares: [CorsMiddleware, LoggingMiddleware] // ✅ Works
});Step 4: Update Middleware Signatures
Remove @Res() usage from all middlewares: throw new HttpError for error status, or return value.
Remember: Middlewares work like controller methods. You don't need to call next() unless you want to modify the response.
Step 6: Update Manual Response Handling
Replace Express response methods with Web Standards:
Before:
@Get('/file')
downloadFile(@Res() res: Response) {
res.sendFile('/path/to/file.pdf');
}
@Get('/redirect')
redirect(@Res() res: Response) {
res.redirect('/new-location');
}After:
@Get('/file')
async downloadFile() {
const file = await Deno.readFile('/path/to/file.pdf'); // or fs.readFile
return new Response(file, {
headers: {
'Content-Type': 'application/pdf',
'Content-Disposition': 'attachment; filename="file.pdf"'
}
});
}
@Get('/redirect')
redirect() {
return new Response(null, {
status: 302,
headers: { 'Location': '/new-location' }
});
}Step 7: Update createApp() Usage
If you were using createApp() for custom server setup:
Before:
const app = yasui.createApp({ controllers: [UserController] });
app.use(express.static('public'));
app.listen(3000);After:
import { serve } from 'srvx';
const app = yasui.createApp({ controllers: [UserController] });
serve({
fetch: app.fetch,
port: 3000,
static: { // srvx static file serving
'/': './public'
}
});Step 8: Test Your Application
- Start your server
- Test all endpoints
- Verify middleware behavior
- Check error handling
- Test with different runtimes (Node.js, Deno, Bun)
New Features in v4.x
TLS/HTTPS Support
yasui.createServer({
controllers: [UserController],
port: 443,
tls: {
cert: './certs/cert.pem',
key: './certs/key.pem',
passphrase: 'optional'
}
});HTTP/2 Support (Node.js)
yasui.createServer({
controllers: [UserController],
tls: {
cert: './cert.pem',
key: './key.pem'
},
runtimeOptions: {
node: {
http2: true // Enabled by default with TLS
}
}
});Multi-Runtime & Edge Deployment
Same code works across runtimes and platforms:
// Traditional runtimes
// Works on Node.js, Deno, and Bun
yasui.createServer({
controllers: [UserController],
port: 3000
});
// Edge runtimes - use createApp()
const app = yasui.createApp({
controllers: [UserController]
});
// Cloudflare Workers
export default {
fetch: app.fetch
};
// Vercel Edge Functions
export const GET = app.fetch;
export const POST = app.fetch;
// Deno Deploy
Deno.serve(app.fetch);
// Netlify Edge Functions
export default app.fetch;Deploy Anywhere
Since YasuiJS returns a standard fetch handler, you can deploy to:
- Traditional servers: Node.js, Deno, Bun
- Edge runtimes: Cloudflare Workers, Vercel Edge, Netlify Edge, Deno Deploy
- Serverless: AWS Lambda (with adapters), Vercel Functions, Netlify Functions
- Any platform that supports Web Standards fetch handlers
Getting Help
If you encounter issues during migration:
- Check the documentation
- Review the examples
- Open an issue on GitHub