sigildocs

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:

ModulePurpose
(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:

PatternMatchesExample
/usersLiteral path/users only
/users/:idNamed parameter/users/42, /users/alice
/static/*pathWildcard (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)))
WrapperKeywordsDescription
with-loggingoutput:Log requests with method, path, status, duration
with-corsallow-origin:, allow-methods:, allow-headers:Add CORS headers (default: allow all origins)
with-content-typedefault:Set default Content-Type (default: "text/html")
with-not-foundbody: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:

OptionDescription
pathCookie path (e.g., "/")
domainCookie domain
max-ageSeconds until expiration
expiresExpiration date string
secureOnly send over HTTPS (#t/#f)
http-onlyNot 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:

OptionDefaultDescription
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)