Built-in req.parseJsonBody() and res.json(data) helpers.
Configurable CORS middleware with origins, methods, headers, credentials.
Per-IP rate limiting with configurable window and limits.
CSP, X-Frame-Options, HSTS, X-Content-Type-Options, Referrer-Policy.
Auto-generate or pass-through X-Request-ID for tracing.
X-Response-Time header on every response.
Reject oversized request bodies with 413 error.
SIGINT/SIGTERM handled cleanly, existing requests finish.
Connection reuse with configurable timeout.
Uncaught exceptions return 500 JSON, not crash.
Full WebSocket support built from scratch. Zero deps.
Built-in /health endpoint returning {"status":"ok"}.
import servy
when isMainModule:
var router = initRouter()
proc handleHello(req: Request, res: Response): Future[void] {.async.} =
res.json(%*{"message": "hello world!"})
router.addRoute("/hello", handleHello)
let opts = newServerOptions(port = 9000)
var s = initServy(opts, router)
s.run()
router.addRoute("/user/:user", handleUser)
router.addRoute("/multi/:first/:second", handleMulti)
router.addRoute("/api", handleApi, HttpPost)
Captured params: req.urlParams["user"]
Query params: req.queryParams["page"]
# Parse request body
let body = req.parseJsonBody()
# Send JSON response
res.json(%*{"status": "ok"})
res.json(%*{"error": "not found"}, Http404)
Production middleware included out of the box:
import servy/middleware_prod
let cors = newCorsMiddleware()
let limiter = newRateLimiter(limit = 100, windowSeconds = 60)
let security = newSecurityHeadersMiddleware()
let reqId = requestIdMiddleware()
let timing = requestTimingMiddleware()
let gzip = gzipMiddleware()
var s = initServy(opts, router, @[reqId, security, cors, timing])
Configurable origins, methods, headers, credentials.
Per-IP rate limiting with sliding window.
CSP, HSTS, X-Frame-Options, X-XSS-Protection.
X-Request-ID header for distributed tracing.
X-Response-Time header.
Content-Encoding for large responses.
413 error for oversized bodies.
Apache-style access logs.
let opts = newServerOptions(
address = "0.0.0.0",
port = 8080,
debug = true,
maxBodySize = 10 * 1024 * 1024,
requestTimeout = 30,
keepAlive = true
)
Built-in. No external dependencies.
proc handleWS(req: Request, res: Response): Future[void] {.async.} =
var ws = await newServyWebSocket(req)
await ws.send("Welcome!")
while ws.readyState == Open:
let msg = await ws.receiveStrPacket()
await ws.send("echo: " & msg)
router.addRoute("/ws", handleWS, HttpGet)