Routing
Request routing, middleware, cookies, and static files.
(import (sigil web)
(sigil http))The (sigil web) module re-exports all routing, middleware, cookie, and static file functionality. Import sub-modules directly for selective access:
| Module | Purpose |
|---|---|
(sigil web routes) | Path-based routing |
(sigil web middleware) | Middleware composition |
(sigil web cookies) | Cookie parsing and setting |
(sigil web static) | Static file serving |
Routes
Define routes with an HTTP method, URL pattern, and handler function. Handlers receive a request and return a response (or #f for no match).
(define app
(router
(route GET "/" home-handler)
(route GET "/about" about-handler)
(route POST "/api/login" login-handler)))Available method constants: GET, POST, PUT, DELETE, PATCH, OPTIONS, HEAD, ANY.
ANY matches all HTTP methods.
Pattern Matching
Route patterns support three segment types:
| Pattern | Matches | Example |
|---|---|---|
/users | Literal path | /users only |
/users/:id | Named parameter | /users/42, /users/alice |
/static/*path | Wildcard (rest of path) | /static/css/main.css |
(router
(route GET "/users/:id" user-handler)
(route GET "/files/*path" file-handler))Path Parameters
Extract parameters from matched routes using path-param:
(define (user-handler request)
(let ((id (path-param request "id")))
(http-response/html 200
(string-append "<h1>User " id "</h1>"))))
;; Get all params as an alist
(define (handler request)
(let ((params (path-params request)))
...))Route Combination
routes combines multiple handlers into one. The first handler to return a non-#f response wins.
(define api-routes
(router
(route GET "/api/users" list-users)
(route POST "/api/users" create-user)))
(define page-routes
(router
(route GET "/" home-page)
(route GET "/about" about-page)))
(define app
(routes api-routes page-routes))Middleware
Middleware wraps a handler to add cross-cutting concerns. Two styles are supported.
Chain Style
Chain-style wrappers take a handler and return a wrapped handler, ideal for use with the -> threading macro:
(define app
(-> (routes api-routes page-routes)
(with-logging)
(with-cors)
(with-content-type)
(with-not-found)))| Wrapper | Keywords | Description |
|---|---|---|
with-logging | output: | Log requests with method, path, status, duration |
with-cors | allow-origin:, allow-methods:, allow-headers: | Add CORS headers (default: allow all origins) |
with-content-type | default: | Set default Content-Type (default: "text/html") |
with-not-found | body: | Return 404 if handler returns #f |
;; Custom CORS settings
(-> handler
(with-cors allow-origin: "https://example.com"
allow-methods: "GET, POST"))
;; Custom 404 page
(-> handler
(with-not-found body: "<h1>Page not found</h1>"))Factory Style
Factory-style middleware returns a handler -> handler function, useful for wrap-middleware:
(define app
(wrap-middleware handler
(logger-middleware)
(cors-middleware '("*"))
(not-found-middleware)))Cookies
Parse cookies from requests and set cookies on responses.
;; Get a specific cookie
(cookie-ref request "session_id")
; => "abc123" or #f
;; Get all cookies as a dict
(cookies request)
; => #{ "session_id": "abc123" "theme": "dark" }
;; Set a cookie on a response
(set-cookie response "session_id" "abc123"
'((path . "/")
(http-only . #t)
(max-age . 3600)
(same-site . "Strict")))
;; Delete a cookie (sets max-age=0)
(delete-cookie response "session_id")Cookie options:
| Option | Description |
|---|---|
path | Cookie path (e.g., "/") |
domain | Cookie domain |
max-age | Seconds until expiration |
expires | Expiration date string |
secure | Only send over HTTPS (#t/#f) |
http-only | Not accessible via JavaScript (#t/#f) |
same-site | "Strict", "Lax", or "None" |
Static Files
Serve files from a directory with automatic MIME type detection.
(define static-handler
(make-static-handler "public/"))
;; Use in a router with a wildcard pattern
(define app
(-> (routes
(router (route GET "/" home-handler))
(make-static-handler "public/"
'((prefix . "/static"))))
(with-not-found)))Options for make-static-handler:
| Option | Default | Description |
|---|---|---|
index | "index.html" | Index file for directory requests |
prefix | "" | URL prefix to strip before file lookup |
serve-file serves a single file by path, returning #f if it doesn't exist:
(serve-file "public/favicon.ico")Path traversal attacks (..) are blocked by safe-path?.
Common Patterns
Full Application Setup
(import (sigil web)
(sigil http)
(sigil json))
(define (home-handler request)
(http-response/html 200 "<h1>Home</h1>"))
(define (user-handler request)
(let ((id (path-param request "id")))
(http-response/json 200
(json-encode #{ id: id }))))
(define app
(-> (routes
(router
(route GET "/" home-handler)
(route GET "/users/:id" user-handler))
(make-static-handler "public/"))
(with-logging)
(with-cors)
(with-not-found)))
(http-serve app port: 8080)