Aller au contenu

Migrating from Express

Ce contenu n’est pas encore disponible dans votre langue.

Lacis covers most of what teams reach for express + a handful of middleware packages to do — routing, body parsing, cookies, CORS, rate limiting, validation — with zero runtime dependencies and file-based routing. This guide maps Express concepts to their Lacis equivalents.

ExpressLacis
app.get('/users', h)file routes/users/index.ts exporting GET
express.Router()directory nesting under routes/
req.params / req.queryreq.params / req.query (typed via defineHandler)
express.json() + req.bodyawait req.json() (built in)
express.urlencoded() + req.bodyawait req.form() (built in)
cookie-parser + req.cookiesreq.cookies.get() / res.cookies.set() (built in)
res.json / res.send / res.redirectsame
res.set(name, val)res.setHeader(name, val)
app.use(mw) (global)middleware.beforeRequest or +middleware.global.ts
app.post('/x', auth, h) (per-route)use: [auth] in defineHandler
next() / next(err)return (or return false to stop); throw to error
app.use(errorHandler)hooks.onError + createNotFoundError(...) / sendError helpers
cors packagebuilt-in cors config
express-rate-limitbuilt-in createRateLimit
joi / zod middlewaredefineHandler({ params, query, body })
app.listen(3000)createServer(routesDir, { port: 3000 })

Express registers routes imperatively; Lacis derives them from the filesystem.

const app = express()
app.get('/users', getUsers)
app.post('/users', createUser)
app.get('/users/:id', getUser)

See Routing for the full file conventions.

Express needs express.json() / express.urlencoded() registered before req.body is populated. Lacis parses on demand — no setup.

app.use(express.json())
app.use(express.urlencoded({ extended: true }))
app.post('/users', (req, res) => {
const { name } = req.body
res.status(201).json({ name })
})

Global middleware maps to server config or a +middleware.global.ts file. Per-route middleware — including per-method scoping that Express expresses positionally — maps to use: in defineHandler.

// global
app.use(logger)
// per-route, runs only for this handler
app.post('/users', rateLimit, auth, createUser)

Express has no built-in validation; you reach for joi, express-validator, or a zod middleware. Lacis builds it into defineHandler via Standard Schema, and the same schema feeds your OpenAPI spec.

import { defineHandler } from 'lacis'
import { z } from 'zod'
export const POST = defineHandler({
body: z.object({ name: z.string(), email: z.string().email() }),
handler: async (req, res) => {
const { name, email } = req.body // typed & validated; 400 returned automatically on failure
res.status(201).json({ name, email })
},
})

Express uses a 4-argument error middleware. Lacis catches thrown errors automatically and routes them to the onError hook; typed HTTP error helpers replace manual status codes.

app.use((err, req, res, next) => {
res.status(err.status ?? 500).json({ error: err.message })
})

See Error Handling for the full set of error constructors.

Replace the cors and express-rate-limit packages with built-in config:

import { createServer, createRateLimit } from 'lacis'
createServer('./routes', {
cors: { origin: 'https://myapp.com', credentials: true },
middleware: {
beforeRequest: createRateLimit({ windowMs: 60_000, max: 100 }),
},
})
app.listen(3000, () => console.log('listening'))

The same routes run on Bun, Vercel, Netlify, and Cloudflare by swapping the adapter — see Deployment.

  • A pluggable app.use() stack of arbitrary connect middleware. Express middleware that depends on the connect signature ((req, res, next)) won’t drop in unchanged — port the logic to a beforeRequest/use: function. Most are a few lines.
  • A view/template engine integration. Render with res.html(...) or return JSON for an API-first app.