API Reference
Public vs extension API.
using Mongoosegives the application-facing surface (servers, requests, responses, middleware builders, helpers). The extension protocols below (router protocol, pipeline seam, format encoding, string utilities) are exported byMongoose.Kerneland are reachable asMongoose.<name>or viaimport Mongoose: <name>— both work without being re-exported at the top level, keeping the user namespace clean.
App
Mongoose.App — Type
App — Mongoose.jl web application.
Use `workers=0` for sync (default) or `workers=N` for async worker pool.
# Constructors
```julia
app = App() # sync, dynamic router
app = App(workers=4) # async, 4 workers
app = App(workers=4, queue_size=2048) # async with larger queue
app = App(router=my_router) # bring-your-own router
```
# Configuration keyword arguments
| Keyword | Default | Description |
|------------------------|--------------------|----------------------------------------|
| `workers` | `0` | Worker threads (0 = sync) |
| `queue_size` | `1024` | Max pending requests (async only) |
| `poll_timeout_ms` | `1` | Mongoose poll interval |
| `max_body_bytes` | `MAX_BODY_BYTES` | Max request body size |
| `drain_timeout_ms` | `DRAIN_TIMEOUT_MS` | Graceful shutdown drain |
| `request_timeout_ms` | `0` | Per-request timeout (0 = disabled) |
| `ws_max_frame_bytes` | `MAX_BODY_BYTES` | Max WebSocket frame size |
| `ws_idle_timeout_ms` | `0` | WS idle timeout (0 = disabled) |
| `header_timeout_ms` | `0` | Close conns without complete headers |
| `body_timeout_ms` | `0` | Max time to receive a request body |
| `max_header_bytes` | `64 KiB` | Max request header size (0 = unlimited)|
| `send_buffer_bytes` | `1 MiB` | Unsent bytes per conn (streams + WS) |
| `max_bg_tasks` | `4×workers` | Runaway timed-out tasks before 503 |
| `max_connections` | `0` | Max open connections (0 = unlimited) |
| `router` | `Router()` | Custom router instance |
| `tls` | `nothing` | `TLSConfig` for HTTPS |
# Structure (DESIGN G4)
- `app.config` — immutable `ServerConfig`.
- `app.runtime` — mutable `RunState` (connections, manager, loop, TLS…).
- `app.router`, `app.middlewares`, `app.errors`, `app.services`, … —
build-phase state; registration after `start!` throws `ServerError`.
- `app.executor` — `SyncExecutor` or `AsyncExecutor` (the worker pool).Mongoose.AbstractServer — Type
AbstractServer — the server protocol.
`App` is the built-in implementation; custom server types implement the
same surface (or subtype `App`). Registration helpers (`route!`, `ws!`,
`use!`, `serve!`, `onerror!`, `onstart!`, `onstop!`) and the read-side
router protocol (`freeze!`, `isfrozen`, `matchroute`, `hasroute`,
`haswsroutes`, `getwsendpoint`, `length`) all work without reaching
through `server.router`.Mongoose.Kernel.start! — Function
start!(app; host, port, blocking, tls)Start the HTTP server. Initializes manager, binds listener, spawns workers (if async), runs event loop.
When blocking=true (default), the caller blocks until shutdown, and a delivered InterruptException (Ctrl+C) triggers graceful shutdown (drain + onstop! hooks) before start! returns. Graceful shutdown depends on Julia delivering SIGINT as an exception to the waiting task; process managers that only send SIGTERM bypass it.
Example
app = App(workers=4)
get!(app, "/") do req; json(Dict("ok" => true)) end
start!(app; port=8080)Mongoose.shutdown! — Function
shutdown!(server)Gracefully stop the server: drain requests, stop workers, free resources. In-flight requests and tracked background tasks (see background!, and over-budget handlers from request_timeout_ms) get one shared grace period of drain_timeout_ms ms before teardown; tasks still running after it are left alone, completed ones are dropped.
Mongoose.isrunning — Function
isrunning(server) → BoolWhether the server's event loop is currently running (started and not yet shut down).
Mongoose.url — Function
url(server) → Union{Nothing,String}The bound base URL (e.g. "http://127.0.0.1:8080") while running, nothing otherwise.
Routing
Mongoose.Kernel.Router — Type
Router — default `AbstractRouter` implementation.
Supports: static paths, typed parameters (`:id::Int`), string parameters
(`:slug`), and a catch-all wildcard (`*path`, must be the last segment).
`freeze!` closes the route table (registration throws afterwards) and
compiles the closed table into a `CompiledDispatch` — the contract that
makes the router amenable to AOT/`--trim=safe` builds.Mongoose.Kernel.route! — Function
route!(router, method, path, handler; middleware=nothing, metadata=nothing) → routerRegister an HTTP route. Supports:
- Static:
/health - Typed params:
/users/:id::Int - String params:
/posts/:slug - Wildcard:
/*path(must be last segment)
middleware is scoped to this route (composed with app-global middleware at dispatch time); it accepts nothing, a single middleware/callable, or a vector/tuple of them. metadata is opaque and available for future OpenAPI-style tooling.
Overlapping parametric routes resolve first-registered-first at dispatch; static routes always take precedence over parametric ones.
route!(group, method, path, handler)Add a route to a group. Path is relative to the group's prefix.
Mongoose.Kernel.ws! — Function
ws!(group, path; kwargs...)
ws!(group, path, handler; kwargs...)Add a WebSocket route to a group. The handler may be positional (shorthand for on_message=handler) or passed as the on_message keyword.
Mongoose.Kernel.freeze! — Function
freeze!(server) → serverClose and compile the server's route table: later route!/ws! throw RouteError. See freeze! on routers for the compiled-dispatch details.
freeze!(router) → routerClose the route table: any later route!/ws! throws RouteError. Dispatch keeps working. This is the closed-route contract required by AOT/--trim=safe profiles (a static route table can be compiled once and pruned).
Freezing also compiles the closed table into a CompiledDispatch (see compiled.jl): each fixed route and parametric route gets a pre-baked terminal (handler + scoped middleware fused, with the handler's concrete type captured), and parametric matching runs through a statically-typed chain with no per-request path splitting. The pipeline uses the compiled path via getterminal when compiled !== nothing; matchroute keeps its generic (correct) implementation.
Mongoose.Kernel.isfrozen — Function
isfrozen(server) → BoolWhether the server's route table has been closed by freeze!.
isfrozen(router) → BoolWhether freeze! has been called on this router (registration closed; the table compiled). Custom AbstractRouters default to false.
Mongoose.Kernel.RouteGroup — Type
RouteGroup — A collection of routes sharing a prefix and middleware stack.Mongoose.Kernel.group — Function
group(prefix; middleware=[]) → RouteGroupCreate a new route group with the given path prefix.
Example
api = group("/api/v1", middleware=[bearer(validate_token)]) do g
route!(g, :get, "/users", list_users)
route!(g, :post, "/users", create_user)
group!(g, "/admin", middleware=[require_role("admin")]) do admin
route!(admin, :delete, "/users/:id::Int", delete_user)
end
endMongoose.Kernel.group! — Function
group!(parent, prefix; middleware=[]) do g ... endAdd a nested group to a parent group.
Mongoose.Kernel.mount! — Function
mount!(router_or_app, group)Mount a RouteGroup into a router or app, registering all its routes with the group's prefix applied and its middleware attached as scoped route metadata. The runtime composes scoped middleware with app-global middleware at dispatch time — no per-route closures are created.
Example
api = group("/api/v1") do g
get!(g, "/users", list_users)
post!(g, "/users", create_user)
end
mount!(app, api)Mongoose.Kernel.matchroute — Function
matchroute(server, method, path) → RouteResultResolve a request against the server's router, without running handlers. See matchroute.
matchroute(router, method, path) → RouteResultResolve a request to its exhaustive outcome: Matched(endpoint, handlers, params) when the route serves the method, NoMatch when the path matches nothing, or MethodMismatch{allowed} carrying the route's method bitmask. Exact (static) matches win; parametric routes are scanned in registration order; the "*" catch-all is the final fallback. HEAD is served only by an explicit head! route — there is no auto-HEAD fallback.
Mongoose.Kernel.hasroute — Function
hasroute(server, path) → BoolWhether the server's router owns path (catch-all excluded). See hasroute.
hasroute(router, path) → Booltrue when a concrete (non-catch-all) route owns the path, regardless of method — used by static file serving to avoid shadowing registered routes (the previous match_route_exact semantic).
Mongoose.Kernel.gethandler — Function
gethandler(mm, method) → Union{Nothing, Function}Mongoose.Kernel.getendpoint — Function
getendpoint(mm, method) → Union{Nothing, Endpoint}The endpoint registered for method, or nothing; throws RouteError for a method outside the seven supported HTTP methods.
Mongoose.Kernel.haswsroutes — Function
haswsroutes(server) → BoolWhether the server serves any WebSocket endpoints. See haswsroutes.
haswsroutes(router) → BoolWhether the router serves any WebSocket endpoints. Defaults to false; the default Router answers true once a ws! route is registered.
Mongoose.Kernel.getwsendpoint — Function
getwsendpoint(server, uri) → Union{Nothing,WSEndpoint}Resolve the WebSocket endpoint for uri, or nothing. See getwsendpoint.
getwsendpoint(router, uri) → Union{Nothing,WSEndpoint}Resolve the WebSocket endpoint registered for uri, or nothing when the router serves no matching WS route. Defaults to nothing.
Mongoose.Kernel.Matched — Type
Matched{endpoint,handlers,params} <: RouteResultA successful route match. endpoint is the Endpoint for the requested method, handlers carries the route's method information (a MethodMap, or SingleEndpoint for routers with one handler), and params is the typed parameter tuple.
Mongoose.Kernel.NoMatch — Type
No route matched the path.
Mongoose.Kernel.MethodMismatch — Type
MethodMismatch{allowed::UInt8} <: RouteResultThe path matched but the method isn't registered; allowed is a bitmask of served methods (HEAD is implied by GET). Serialize with allow_from_bitmask for the RFC 9110 §15.5.6 Allow header.
Mongoose.Kernel.RouteResult — Type
RouteResult — outcome of `matchroute`: `Matched`, `NoMatch`, or
`MethodMismatch{allowed}` (the last carries the route's method bitmask,
so 405 `Allow` needs no secondary lookup).Mongoose.Kernel.MethodMap — Type
MethodMap — Fixed-slot storage for HTTP method → endpoint mapping.Mongoose.Kernel.post! — Function
post!(router_or_app, path, handler)Mongoose.Kernel.patch! — Function
patch!(router_or_app, path, handler)Mongoose.Kernel.options! — Function
options!(router_or_app, path, handler)Mongoose.Kernel.head! — Function
head!(router_or_app, path, handler)
get!,put!, anddelete!areBasemethods that Mongoose extends (the other verbs are Mongoose exports). All accept(server_or_router, path, handler)and the do-block formget!(server, path) do req … end.
Request Processing
Mongoose.Kernel.process — Function
process(ctx::RequestContext, request) → ResponseRun the full pipeline: attach services to the request context, dispatch the request through any middleware then the router, apply custom error responses for 4xx/5xx results, and map thrown exceptions (typed onerror! handlers first, then the built-in HTTPError/ValidationError mapping).
Arguments
ctx::RequestContext— router, middleware stack, error pages, DI services, and typed exception handlers (seeRequestContext).request::Request— the transport-agnostic request.
Middleware is composed with the matched route's scoped Endpoint middleware (global → group/route → handler), and it wraps every terminal — including the 404/405 producers — so interception middleware (CORS, health, metrics) observes all requests.
Mongoose.Kernel.RequestContext — Type
RequestContext{R,M,E,S,H} — the app-level request-processing bundle.
Collapses the config that `process` needs into one object so the
pipeline seam has a single argument: the router, the app-global middleware
stack, the status→error-page map, the DI services, and the typed exception
handlers.
The global middleware stack is stored as a **baked tuple snapshot** (built
once by `App`/`use!` and immutable afterward), so the per-request pipeline
never re-grows or re-walks a mutable vector and needs no
`[global; scoped]` concatenation.
Standalone use (no server):
```julia
ctx = RequestContext(router; errors=errs, services=(db=pool,))
resp = process(ctx, Request(:get, "/", Dict{String,String}(), Pair{String,String}[], ""))
```Mongoose.Kernel.getterminal — Function
getterminal(router, request) → Union{Nothing,Function}Optional compiled-dispatch capability. A frozen Router that compiled its route table returns a pre-built terminal (req) → Response for the request (scoped middleware already fused, handler call statically typed); nothing means "fall back to the generic dispatch path".
This is the contract-by-fallback seam that lets the pipeline skip the per-request closure/concat allocation when the router is compiled, without forcing every router implementation to understand compilation.
Mongoose.Kernel.invokeendpoint — Function
invokeendpoint(endpoint, request, params) → ResponseCall the matched endpoint's handler with the route parameters. The built-in Endpoint dispatches handler(req, params...) (or handler(req) when there are none) — that method lives in process.jl, after Endpoint is defined.
Custom routers that carry their own endpoint type implement this method (and scopedmiddleware) instead of forcing their endpoints into Mongoose.Endpoint.
Mongoose.Kernel.scopedmiddleware — Function
scopedmiddleware(endpoint) → TupleScoped middleware owned by a matched endpoint (empty tuple for custom endpoint types). Paired with invokeendpoint for custom routers; the built-in Endpoint stores a captured tuple.
Mongoose.Kernel.errorresponse — Function
errorresponse(errors, status) → ResponseLook up a custom error response, falling back to module defaults. errors maps HTTP status codes to a static Response or Function(req).
Mongoose.Kernel.runpipeline — Function
runpipeline(middlewares, request, handler) → Response
runpipeline(globals, scoped, request, handler) → ResponseRun the middleware onion around handler: each middleware receives (request, next); next advances to the following middleware and finally the handler. Middleware may short-circuit by returning without calling next.
The tuple form (the compiled/production path, where route-scoped middleware is already fused into the terminal) walks the baked stack with an immutable callable continuation — Next is a Function, so the (req, next) contract is unchanged, but no closure or cursor is allocated per request.
The four-argument form (generic/dev path) walks the baked app-global stack and the route-scoped stack with one cursor over their virtual concatenation — no [global; scoped] array built per request.
handler may be any 0-arity callable (Function or functor) — the compiled dispatch path passes pre-baked terminal functors.
Mongoose.Kernel.asmiddleware — Function
asmiddleware(mw) → AbstractMiddlewareNormalize any middleware into an AbstractMiddleware: AbstractMiddleware instances pass through; any other callable f(req, next) is wrapped in a FunctionMiddleware. This is the single admission point used by use!, by route!/group middleware= metadata, and by Endpoints.
Mongoose.Kernel.asmiddlewares — Function
asmiddlewares(input) → Vector{AbstractMiddleware}Normalize middleware input into a Vector{AbstractMiddleware}: nothing → empty, a single middleware (or plain callable) → one entry, a vector/tuple → one entry per element. Each element goes through asmiddleware.
Mongoose.Kernel.attach! — Function
attach!(middleware, server) → middlewareOptional lifecycle hook: use! calls it when middleware is registered, so middleware that needs server state (metrics gauges, readiness checks) can capture a reference. Default is a no-op.
Mongoose.Kernel.AbstractRequest — Type
AbstractRequest — protocol for transport-agnostic requests.
`Request` is the default implementation. Transport adapters construct
their own request objects (or reuse `Request`) and the protocol layer
never crosses into FFI types.Request & Response
Mongoose.Kernel.Request — Type
Request — Full HTTP request with owned data.
`query` is parsed lazily: the transport stores the raw query string and
[`parsequery`](@ref) memoizes the parsed `Dict` on first access, so requests
that never read the query pay only the raw-string copy (nothing at all when
there is no query). `context` is allocated on first access the same way.
`services` is the app's DI NamedTuple, set by `process` before dispatch (no
dict allocation, no boxing); [`service`](@ref)/[`withservices`](@ref) read
it directly.
`remote_addr` is the transport-provided peer address (the client's IP as a
string, or `nothing` when the transport does not supply one — e.g. the
standalone pipeline or `FakeTransport`-constructed requests).Mongoose.Kernel.Response — Type
Response — Immutable HTTP response with structured headers.
Headers are stored as a `Vector{Pair{String,String}}` and serialized
to wire format only when sent. Middleware can inspect and modify headers
before they reach the client.Mongoose.Kernel.StreamResponse — Type
StreamResponse — Response whose body is produced incrementally.
The `producer` function receives a `StreamWriter` and writes chunks to it.
The producer runs on its own task, feeding a bounded channel that the
event loop drains — a slow producer never blocks the poll thread or other
connections. Works in both sync and async modes.
# Example
```julia
function sse_handler(req)
StreamResponse(200, "text/event-stream") do writer
for i in 1:10
write(writer, "data: event $i\n\n")
flush(writer)
sleep(0.5)
end
end
end
```Mongoose.Kernel.Headers — Type
Headers — lightweight wrapper around the parsed header pairs.
Ownership of the type by Mongoose allows defining `get`/`haskey`
without committing Base piracy on `Vector{Pair{String,String}}`.Mongoose.Kernel.asheaders — Function
asheaders(input) → HeadersNormalize accepted header inputs into Headers: a Headers passes through, a single Pair or a vector/tuple of pairs is converted (string keys/values are Stringed; anything else throws ArgumentError).
Mongoose.Kernel.mergeheaders — Function
mergeheaders(headers::Headers, extra; prepend=false) → Headers
mergeheaders(response::Response, extra; prepend=false) → ResponseReturn a new headers list (or a new Response carrying it) with extra merged in; the original is left untouched. extra may be a single pair or a vector of pairs.
This is a concatenation, not a dictionary merge: existing pairs are preserved (including duplicates and order). extra is appended after them by default; prepend=true places extra first so get/haskey find those values before any same-named pair the original carried.
Mongoose.Kernel.Cookie — Type
Cookie — HTTP Set-Cookie parameters.Response Helpers
Mongoose.Kernel.json — Function
json(data; status=200, headers=[]) → ResponseCreate a JSON response. Supports Dict, NamedTuple, Vector, and any JSON-serializable type.
Example
json(Dict("id" => 1, "name" => "Alice"))
json((id=1, name="Alice")) # NamedTuple
json(Dict("error" => "Not Found"); status=404)Mongoose.Kernel.html — Function
html(content; status=200, headers=[]) → ResponseMongoose.Kernel.text — Function
text(content; status=200, headers=[]) → ResponseMongoose.Kernel.redirect — Function
redirect(url; status=302, headers=[]) → ResponseReturn an HTTP redirect response.
Example
redirect("/login")
redirect("https://example.com"; status=301)Mongoose.Kernel.setcookie — Function
setcookie(cookie) → StringSerialize a Cookie to a Set-Cookie header value string.
Request Helpers
Mongoose.Kernel.query — Function
query(req, key) → Union{String, Nothing}
query(req, key, default) → String
query(req, key, default::T) → T (auto-parses to type of default)Type-safe query parameter access with optional default and auto-parsing.
Examples
q = query(req, "q", "") # String with default
page = query(req, "page", 1) # Auto-parse to Int
limit = query(req, "limit", 20) # Auto-parse to Int
flag = query(req, "debug", false) # Auto-parse to BoolMongoose.Kernel.body — Function
body(req) → StringReturn the raw request body.
Mongoose.Kernel.parsejson — Function
parsejson(req) → AnyParse the request body as JSON. Throws BadRequestError (400) on malformed JSON so the pipeline answers 400 instead of falling through to a 500.
Mongoose.Kernel.parseform — Function
parseform(req) → Dict{String,String}Parse an application/x-www-form-urlencoded request body.
Mongoose.Kernel.parsemultipart — Function
parsemultipart(req) → Dict{String, Union{String, MultipartFile}}Parse a multipart/form-data request body. Returns a Dict where string fields map to their values and file fields map to MultipartFile objects.
Example
post!(app, "/upload") do req
parts = parsemultipart(req)
file = parts["avatar"]::MultipartFile
text("Received $(file.filename) ($(length(file.data)) bytes)")
endMongoose.Kernel.MultipartFile — Type
MultipartFile — Represents a file uploaded via multipart/form-data.Mongoose.Kernel.parsecookies — Function
parsecookies(req) → Dict{String,String}Parse cookies from the request Cookie header.
Mongoose.Kernel.context — Function
context(req) → Dict{Symbol,Any}Return the request's per-request context dict, creating it lazily on first access.
Example
get!(app, "/") do req
ctx = context(req)
ctx[:user_id] = 42
json(Dict("ok" => true))
endMongoose.Kernel.header — Function
header(req, name) → Union{String,Nothing}Look up a request header by name (case-insensitive).
Mongoose.service — Function
service(req, name) → Any
service(req, ::Val{name}) → T
service(req, name, T) → TRetrieve a service by name from the request context.
service(req, :db)returns the raw value (values may be zero-arg callables, which are invoked).service(req, Val(:db))avoids re-parsing the name, but the lookup goes through the dynamic request context — the return type is inferredAny.service(req, :db, DBPool)asserts the type and throws otherwise.
For type-stable access in hot paths use withservices, whose closure receives the concrete NamedTuple:
Example
app = App(services=(db=pool, cache=redis))
withservices(req) do svcs
svcs.db # concrete: DBPool
end
db = service(req, Val(:db)) # convenient, dynamically typedMongoose.services — Function
services(req) → NamedTupleThe request's DI services as a NamedTuple (empty when none were registered). Dynamically typed at this boundary; use withservices for type-stable access.
Mongoose.withservices — Function
withservices(f, req) → f(services(req))Function-barrier access to DI services: the closure receives the concrete NamedTuple, so field access inside it specializes — unlike service(req, Val(:x)), which returns a dynamically typed value.
Example
withservices(req) do svcs
svcs.db.query("select 1")
endURI & String Utilities
Mongoose.Kernel.parsequery — Function
parsequery(query::AbstractString) → Dict{String,String}Single-pass query string parser. Pre-sized for typical query parameter counts.
parsequery(req) → Dict{String,String}The request's query parameters, parsed from the raw query on first access and memoized. Prefer query for typed lookups; use this when you need the whole dict. (The string method parsequery(str) is the underlying parser.)
Mongoose.Kernel.stripquery — Function
stripquery(uri) → SubStringReturn the URI path without the query string. Zero-allocation (returns a view).
Mongoose.Kernel.formatheaders — Function
formatheaders(headers) → StringSerialize headers into Mongoose C library format: "Key: Value\r\n".
formatheaders(io, headers)Write the Mongoose header framing for headers into io — used to assemble a response header block in one buffer (no intermediate String).
Mongoose.Kernel.urldecode — Function
urldecode(bytes, start_i, end_i; plus=true) → StringDecode a URL-encoded byte range. Handles + → space (form semantics) and %XX hex escapes. Pass plus=false for path segments, where + is a literal character (RFC 3986 §2.3).
Mongoose.Kernel.asstrings — Function
asstrings(input) → Vector{String}Normalize string-ish input into a Vector{String}: nothing → empty, a single AbstractString → one-element vector, a vector/tuple → Stringed entries. Used for paths=, allowed_origins=, and similar collection kwargs.
Mongoose.Kernel.statusreason — Function
HTTP status code → reason phrase mapping.
Inlined chain for hot-path status codes.Middleware
Mongoose.use! — Function
use!(app, middleware; paths=[])Add middleware to an app. middleware may be any callable (req, next) → Response or an AbstractMiddleware subtype; plain functions are wrapped automatically. When paths is non-empty, the middleware only applies to requests whose URI starts with one of the given prefixes.
Example
use!(app, cors())
use!(app, bearer(validate_token); paths=["/api"])
use!(app, (req, next) -> (req.headers ...; next()))Mongoose.Kernel.cors — Function
cors(; origins, allow_methods, allow_headers, max_age_seconds, allow_credentials)Create a CORS middleware. origins accepts "*", a single origin, or a Vector{String} of allowed origins.
Keyword Arguments
origins: Allowed origins (default"*"; a list enables per-origin echo).allow_methods::String: Allowed HTTP methods (default"GET, POST, PUT, PATCH, DELETE, OPTIONS").allow_headers::String: Allowed request headers (default"Content-Type, Authorization").max_age_seconds::Int: Preflight cache duration in seconds (default86400).allow_credentials::Bool: EmitAccess-Control-Allow-Credentials(defaultfalse).
Example
use!(app, cors(origins=["https://myapp.com", "https://admin.myapp.com"],
allow_credentials=true, max_age_seconds=600))Mongoose.Kernel.ratelimit — Function
ratelimit(; max_requests, window_seconds, trust_proxies, key_fn)Create a rate-limiting middleware using a sharded fixed-window counter. Returns 429 Too Many Requests when the limit is exceeded.
Uses 16 independent shards internally to minimize lock contention.
Keyword Arguments
max_requests::Int: Maximum requests allowed per window (default:100).window_seconds::Int: Time window duration in seconds (default:60).trust_proxies::Bool: TrustX-Forwarded-For/X-Real-IPfor the default bucket key (default:false). Enable only behind a proxy that overwrites these headers; otherwise clients can spoof their bucket. Without it the default key is the request's remote address (per-client host).key_fn::Function: Custom bucket key(Request) -> String, overriding the default IP-based key (e.g. an API key extracted from the request).
Example
use!(server, ratelimit(max_requests=50, window_seconds=30))
use!(server, ratelimit(max_requests=100, key_fn=req -> apikey(req)))Mongoose.Kernel.bearer — Function
bearer(validator)
bearer(secret::String)Create a Bearer token authentication middleware. When called with a function, validator(token) must return true if valid. When called with a string, uses constant-time comparison to prevent timing attacks.
Example
use!(server, bearer("my-secret-token"))
use!(server, bearer(token -> token in valid_tokens))Mongoose.Kernel.apikey — Function
apikey(keys; header_name="X-API-Key")
apikey(; header_name="X-API-Key", keys)Create an API key authentication middleware. keys may be a single key string or a collection of keys (Set/Vector); it is normalized to a Set{String} at construction. The positional and keyword forms are equivalent.
Example
use!(server, apikey("key-123"))
use!(server, apikey(Set(["key-123", "key-456"])))
use!(server, apikey(["key-123"]); header_name="x-api-key")Mongoose.Kernel.basicauth — Function
basicauth(user, password; realm="restricted")Create an HTTP Basic authentication middleware with constant-time credential comparison.
Example
use!(server, basicauth("admin", ENV["ADMIN_PASSWORD"]))Mongoose.Kernel.logger — Function
logger(; threshold_ms=0, output=stderr, structured=false)Create a request-logging middleware.
Keyword Arguments
threshold_ms::Int: Only log requests slower than this (default:0= log all) in milliseconds.output::IO: IO stream for log output (default:stderr).structured::Bool: Iftrue, emit one JSON object per line (default:false).
Example
use!(server, logger()) # plain text, all requests
use!(server, logger(threshold_ms=100)) # only slow requests
use!(server, logger(structured=true)) # JSON structured logsMongoose.Kernel.health — Function
health(; health_check, ready_check, live_check)Create a health check middleware for cloud-native deployments.
Keyword Arguments
health_check::Function: Function that returnstrueif the service is healthy (default: always true)ready_check::Function: Function that returnstrueif the service is ready to accept traffic (default: always true)live_check::Function: Function that returnstrueif the service is alive (default: always true)
Endpoints
GET /healthz: Overall health status (combines all checks)GET /readyz: Readiness for traffic (load balancers)GET /livez: Liveness check (process alive)
Example
use!(server, health(
health_check = () -> check_database(),
ready_check = () -> check_dependencies(),
live_check = () -> true # Process is always alive if running
))Mongoose.Kernel.metrics — Function
metrics(; path="/metrics")Create a Prometheus-compatible metrics middleware.
Exposes an HTTP endpoint (default /metrics) in Prometheus text exposition format v0.0.4. Uses 8 internal shards keyed by thread ID to minimize lock contention under concurrent load.
Keyword Arguments
path::String: Scrape endpoint path (default:"/metrics").
Metrics
| Metric | Type | Labels |
|---|---|---|
http_requests_total | counter | method, status |
http_request_duration_seconds | histogram | le (11 buckets: 5ms–10s) |
mongoose_connections | gauge | open connections |
mongoose_ws_clients | gauge | open WebSocket clients |
mongoose_active_streams | gauge | in-flight streaming responses |
mongoose_executor_inflight | gauge | jobs currently executing |
mongoose_executor_queue_depth | gauge | jobs waiting in the executor queue |
mongoose_bg_tasks | gauge | abandoned timed-out handler tasks |
mongoose_ws_frames_dropped | gauge | WebSocket pushes dropped (slow readers) |
Gauges are emitted once the middleware is registered (use! attaches the server); before that only the counter and histogram are exposed.
Example
app = App(workers=4)
use!(app, health())
use!(app, metrics()) # exposes GET /metrics
start!(app; port=8080)Prometheus scrape_configs:
- job_name: myapp
static_configs:
- targets: ['localhost:8080']
metrics_path: /metricsMongoose.Kernel.security — Function
security(; hsts_max_age_seconds, frame_options, content_type_options, referrer_policy, csp)Create a security headers middleware. All headers are pre-computed at construction time. Each optional header is disabled with nothing (uniform "off" convention); only content_type_options is a plain Bool.
Keyword Arguments
hsts_max_age_seconds::Union{Nothing,Int}: HSTS max-age in seconds (default: 31536000 = 1 year).nothingdisables the header.frame_options::Union{Nothing,String}: X-Frame-Options value (default: "DENY").content_type_options::Bool: Add X-Content-Type-Options: nosniff (default: true).referrer_policy::Union{Nothing,String}: Referrer-Policy value (default: "strict-origin-when-cross-origin").csp::Union{Nothing,String}: Content-Security-Policy value (default: nothing = not added).
Mongoose.Kernel.compress — Function
compress(; min_size_bytes=1024) → CompressCreate a GZip compression middleware. Only compresses responses larger than min_size_bytes bytes when the client sends Accept-Encoding: gzip.
Keyword Arguments
min_size_bytes::Int: Minimum response body size to compress (default:1024bytes).
Example
use!(app, compress())
use!(app, compress(min_size_bytes=256)) # More aggressive compressionMongoose.Kernel.etag — Function
etag() — add ETag + conditional request middleware (304/412).
# Example
```julia
use!(app, etag()) # before compress, after cors
use!(app, compress(min_size_bytes=1024))
```Mongoose.Kernel.AbstractMiddleware — Type
Middleware protocol — composable request/response pipeline.
Middleware is a callable `(req, next) → response` that wraps around the
handler ("onion" model). `req` is the `Request`, `next` advances to the
following middleware (and finally the handler); returning a `Response`
without calling `next` short-circuits.
# Implementing Middleware
Subtype `AbstractMiddleware` and implement the call operator:
```julia
struct MyMiddleware <: AbstractMiddleware end
function (mw::MyMiddleware)(req::Request, next::Function)
# before logic
response = next() # call downstream
# after logic
return response
end
```
Plain closures/functions work too: `use!`/`route!` wrap them via
`asmiddleware` (see `FunctionMiddleware`). The tag type exists so the
pipeline can hold a typed stack (`Vector{AbstractMiddleware}`).Mongoose.Kernel.FunctionMiddleware — Type
FunctionMiddleware{F} — adapter that lets any callable `f(req, next)` act
as a middleware without subtyping `AbstractMiddleware`.
User code rarely needs this directly: `use!` and `route!(; middleware=...)`
accept plain closures/functions and wrap them automatically.Mongoose.Kernel.Logger — Type
Logger — Request logging middleware.
Logs each request's method, URI, status code, and elapsed time.Mongoose.Kernel.Health — Type
Health — Health check middleware for cloud-native deployments.
Intercepts `/healthz`, `/readyz`, and `/livez` before any other middleware.Mongoose.Kernel.Metrics — Type
Metrics — Prometheus-compatible metrics middleware.
Intercepts every request, records latency and status, and exposes the
configured `/metrics` endpoint in Prometheus text exposition format.
Metrics exposed:
- `http_requests_total{method,status}` — counter
- `http_request_duration_seconds{le}` — histogram (11 finite buckets)Mongoose.Kernel.Security — Type
Security — adds standard security headers to all responses.
Protects against common web vulnerabilities (XSS, clickjacking, MIME
sniffing).Mongoose.Kernel.Etag — Type
Etag — middleware that computes strong ETags and handles conditional requests.
Adds an `ETag` header to generated buffered responses (deterministic
FNV-1a over the response body bytes — stable across restarts, no new
dependencies), and applies RFC 9110 §13 preconditions:
- `If-None-Match` match → `304 Not Modified` (GET/HEAD) or
`412 Precondition Failed` (other methods); `"*"` matches any tag.
- `If-Match` mismatch → `412 Precondition Failed` (strong comparison).
- `If-None-Match` uses weak comparison (`W/` prefixes are stripped).
Streaming responses and raw (non-`Response`) handler returns are passed
through unchanged — they have no serialized body to hash. Register it
outside transformation middleware (e.g. before `compress`) so the ETag
validates the representation that is actually sent.Mongoose.Kernel.Cors — Type
Cors — CORS (Cross-Origin Resource Sharing) middleware.
Handles preflight OPTIONS requests and attaches CORS headers to responses:
- origin allowlist: only listed origins get `Access-Control-Allow-Origin`,
reflected verbatim (with `Vary: Origin`); `"*"` still means any origin.
- credentials: opt-in `Access-Control-Allow-Credentials: true`
(`"*"` + credentials is not emitted, per the spec).
- preflight validation: requested method and request headers are checked
against the allowlists; mismatches get a 403 instead of a blanket 204.Mongoose.Kernel.Bearer — Type
Bearer — Bearer token authentication middleware.
Checks the `Authorization: Bearer <token>` header and delegates validation to a user-supplied function.Mongoose.Kernel.ApiKey — Type
ApiKey — API key authentication middleware.
Reads a header by name and checks it against a set of valid keys.Mongoose.Kernel.BasicAuth — Type
BasicAuth — HTTP Basic authentication middleware.
Validates `Authorization: Basic <base64(user:password)>` with
constant-time comparison.Mongoose.Kernel.RateLimit — Type
RateLimit — fixed-window rate limiting middleware with sharded locks.
Tracks requests per bucket key with automatic cleanup, using N shards
(each with its own lock) to reduce contention under high concurrency.
Buckets are keyed by a user-supplied `key_fn(request) -> String`, or by a
default that keys on the request's transport-provided remote address;
`X-Forwarded-For`/`X-Real-IP` are only trusted when `trust_proxies=true`
(reading proxy headers directly is a spoofing vector when the app is
exposed to clients).Mongoose.Kernel.Compress — Type
Compress — GZip compression middleware.
Compresses response bodies when the client supports it
(Accept-Encoding: gzip). Skips already-compressed, streaming, or small
responses.Server-Sent Events
Mongoose.Kernel.SSEWriter — Type
SSEWriter — Writes Server-Sent Events to a connection.
Used within a `StreamResponse` producer:
```julia
function sse_handler(req)
sse(req) do writer
for i in 1:10
emit(writer; data="tick $i", event="heartbeat", id=string(i))
sleep(1.0)
end
end
end
```Mongoose.Kernel.emit — Function
emit(sse; data, event="", id="", retry_ms=nothing)Send a single SSE event to the client. Fields:
data::String— event payload (required). Multi-line data is handled correctly.event::String— event type/name (optional).id::String— event ID for reconnection (optional).retry_ms::Union{Nothing,Int}— reconnection interval in ms (optional).
Mongoose.Kernel.sse — Function
sse([req,] producer; headers=[]) → StreamResponseConvenience constructor for SSE responses with correct headers.
function stream_events(req)
sse(req) do writer
emit(writer; data="hello", event="greeting")
end
endWebSocket
Mongoose.Kernel.Message — Type
Message — one WebSocket frame.
`data` is either the UTF-8 text of a text frame or the raw bytes of a
binary frame. WebSocket handlers receive a `Message` and may return a
`Message` to reply (or `nothing` for no reply).Mongoose.broadcastws — Function
broadcastws(server, path, data)Server-initiated WebSocket push: enqueue a text frame for every open client of path (async executors only — the frame is routed through the same reply queue the worker pool uses, so it is sent on the poll/callback thread and is safe to call from any task). Idle/closed clients are skipped naturally: a stale conn id is dropped when drained.
Lifecycle
Mongoose.onerror! — Function
onerror!(app, status, handler)
onerror!(handler, app, status)Register a custom error handler for a specific HTTP status code. handler may be a Response (static) or Function(req) → Response (dynamic). Both argument orders are accepted; the (handler, app, status) form exists so a do-block works.
Example
onerror!(app, 404) do req
json(Dict("error" => "not found", "path" => req.uri); status=404)
endonerror!(app, ::Type{E}, handler)Register a typed exception handler: handler(req, e) returns the Response for any handler/middleware error that is a E (or subtype). Handlers are tried in registration order; unhandled exceptions fall through to the default 500 path.
Example
struct NoMatch <: Exception end
onerror!(app, NoMatch) do req, e
json(Dict("error" => "not found"); status=404)
endMongoose.onstart! — Function
onstart!(app, f)
onstart!(f, app)Register a callback to run after the server starts (before accepting connections). Both argument orders are accepted; the (f, app) form exists so onstart!(app) do … end works.
Mongoose.onstop! — Function
onstop!(app, f)
onstop!(f, app)Register a callback to run during graceful shutdown. Both argument orders are accepted; the (f, app) form exists so onstop!(app) do … end works.
Mongoose.service! — Function
service!(app, name, value)Register a service for dependency injection.
service!(app, :db, MyDB.connect())
service(req, :db) # retrieve inside handlerMongoose.background! — Function
background!(app, f)
background!(f, app)Schedule a background task to be spawned when start! is called. f should be a zero-argument function. Both argument orders are accepted; the (f, app) form exists so background!(app) do … end works.
background!(app) do
while true
cleanup_expired_sessions!()
sleep(60)
end
endMongoose.serve! — Function
serve!(server, directory; uri_prefix="/")Serve static files from directory under uri_prefix on the URL. The directory is the positional argument; the URL prefix is always the keyword (so the two can never be silently swapped).
Dotfiles (.env, .git, …) are not served; .well-known is allowed for ACME challenges and security.txt. Symlinks inside the directory are followed by the OS, so do not place links that escape the root.
Configuration
Mongoose.ServerConfig — Type
ServerConfig — Immutable tuning parameters for the server.
Separated from `App` runtime state to clearly distinguish what's
configuration (set once) vs what changes during execution.Mongoose.TLSConfig — Type
TLSConfig(; cert, key, ca="", name="", skip_verification=false)TLS options for HTTPS/WSS servers, passed to start!(app; tls=TLSConfig(...)). cert, key, and ca accept file paths, PEM strings, or raw bytes.
The Mongoose_jll build uses Mongoose's built-in TLS, which is TLS 1.3 only — clients pinned to TLS 1.2 (older runtimes, some corporate proxies) cannot connect; terminate at a reverse proxy or rebuild the JLL against OpenSSL if you need 1.2.
Setting ca enables mutual TLS: the server then requires a client certificate signed by that CA (a handshake without one fails). TLS handshakes have no C-level timeout, so configure header_timeout_ms (the accept-time sweep reclaims stalled handshakes) and max_connections on public TLS listeners.
Extensibility
Mongoose.Kernel.AbstractRouter — Type
AbstractRouter — pluggable routing protocol.
Any type `R <: AbstractRouter` can be supplied to `App(router=R())`.
The transport and dispatch layers talk to routers exclusively through
this protocol; they never inspect internal fields.
Required protocol for HTTP dispatch:
- `route!(r::R, method, path, handler; middleware=AbstractMiddleware[], metadata=nothing) → r`
(register an HTTP route; the router stores the handler inside an
`Endpoint` and never interprets it. The `App` layer normalizes
`middleware` to a vector before delegating, so a router may assume an
`AbstractVector`)
- `matchroute(r::R, method, path)` → a `RouteResult`
(`Matched` / `NoMatch` / `MethodMismatch{allowed}` — 404/405 and the
`Allow` set are resolved by the router at match time)
- `hasroute(r::R, path)` → `Bool` (path owned, catch-all excluded)
(no `"*"` fallback — route ownership check for static serving)
- `gethandler(match, method)` / `getendpoint(match, method)` — work on
any `Matched`, including custom routers' matches and `SingleEndpoint`
Custom routers return a `RouteResult` from `matchroute`. Returning a
`Matched` directly is enough: wrap the endpoint with
`SingleEndpoint(ep, method)` as the match's `handlers` value.
Optional capabilities (safe defaults are provided):
- `haswsroutes(r::R) → Bool` (default: `false`)
- `ws!(r::R, path; ...)` (no default)
- `getwsendpoint(r::R, uri)` (default: `nothing`)
- `length(r::R)` (default: `0`)
The default implementation is `Router` in `router.jl`.
## Contract-by-fallback pattern
The `AbstractRouter` protocol is enforced by *fallback methods*: calling a
protocol function on an unimplemented router type throws a `MethodError`
naming the missing method, so a partially-implemented router fails loudly
instead of silently under-dispatching. Optional capabilities default to
"not supported" rather than throwing.Mongoose.Kernel.SingleEndpoint — Type
SingleEndpoint — minimal "handlers" carrier for custom routers that don't
track per-method maps. `gethandler`/`getendpoint` answer for exactly the
one method the endpoint serves.Mongoose.Kernel.Endpoint — Type
Endpoint{F} — what a route owns: handler, scoped middleware, and metadata.
The handler's concrete type is a type parameter, so the endpoint can be
invoked without a dynamic call when its type is known (custom routers,
compiled terminals); registration specializes on the handler type.
The router only stores and returns `Endpoint`s; it does not execute
middleware. `middleware` applies to this route (in addition to app-global
middleware); `metadata` is opaque and available for OpenAPI-style docs.Mongoose.Kernel.AbstractExecutor — Type
AbstractExecutor — controls where and how middleware + handlers run.
The executor is the "where/how to run" half of the runtime. It owns the
concurrency policy (inline vs worker pool), backpressure, and lifecycle of
execution, so the transport and `App` never see `Channel`s or `Task`s.
Handler jobs are closures `() → Union{Nothing, <:Tagged}` producing the
reply to deliver (or `nothing` to drop). The executor only runs them and
ships replies; it never interprets `Request`/`Response`/`Intent`.
Reference implementations:
- `SyncExecutor` — runs each job inline on the calling thread.
- `AsyncExecutor` (server layer) — bounded worker pool + reply queue.
## Contract
- `submit!(executor, job)` — schedule a job. `SyncExecutor` runs it
inline and returns its value; pool executors return nothing.
- `start!(executor, app)` / `stop!(executor)` — lifecycle.
- `haspending(executor)` — is work still in flight? (drain support)
Missing methods fail loudly via fallback methods below.Mongoose.Kernel.SyncExecutor — Type
SyncExecutor — runs every job inline on the calling thread.
This is the transport-agnostic equivalent of `workers=0` sync mode.Mongoose.AsyncExecutor — Type
AsyncExecutor — bounded worker pool with a reply queue.
Architecture:
- Transport submits closures (`() → Union{Nothing,Kernel.Tagged}`) via `submit!`.
- `workers` threads dequeue and run jobs, pushing replies onto `replies`.
- The event loop drains `replies` (see `dispatch_replies!`) and sends them
through the transport's connections.
- `supervise_workers!` respawns dead workers on a timer.
The executor never interprets requests/responses; it only runs jobs and
ships the replies they produce. Timeout policy is applied by the transport
when it builds a job.Mongoose.Kernel.stop! — Function
stop!(exec::AsyncExecutor; timeout=5.0)Stop the worker pool. calls is closed first, then replies are drained while workers are joined so a full reply queue cannot deadlock the join. The join is bounded by timeout seconds: a handler that never returns is abandoned (its task keeps running, as before) instead of hanging shutdown forever.
Mongoose.Kernel.FakeExecutor — Type
FakeExecutor — deterministic, thread-free executor for tests.
Mirrors the `AsyncExecutor` contract (queued `submit!`, `haspending`,
`start!`/`stop!`) but never spawns workers: jobs are queued and only run
when the test calls `run!`, inline in submission order. This makes
reply-order/backpressure tests deterministic — no threads, no sleeps.
```julia
fe = FakeExecutor()
submit!(fe, () -> "first")
submit!(fe, () -> "second")
@assert haspending(fe)
@assert run!(fe) == ["first", "second"]
@assert !haspending(fe)
```Mongoose.Kernel.run! — Function
run!(fe::FakeExecutor) → Vector{Any}Run every queued job inline in submission order, clearing the queue and recording each result.
Mongoose.Kernel.AbstractTransport — Type
AbstractTransport — replaceable I/O transport.
The transport is the last FFI boundary: it converts external events into
`Kernel` objects and responses into bytes/frames, and the runtime
never sees `Ptr{Cvoid}`.
Today the transport is a **capability-tagged seam** rather than a callable
interface: implementations declare what they can do via the ability
traits (`canws`, `cantls`, `canstream`), and the server
drives the one C implementation
(`transport/mongoose`). A reference fake (`FakeTransport`, in
`testing.jl`) drives the whole pipeline with no FFI.
The C transport's concrete lifecycle entry points (used by `start!` /
`shutdown!`) are `init_server!`, `bind_server!`,
`spawn_event_loop!`/`stop_event_loop!`, and its send path is
`send_http_response!`/`send_ws_frame!`/`send_stream_response!` in
`transport/mongoose`. Extracting a full `init!/listen!/poll!/send!`
interface behind `AbstractTransport` is deferred: there is a single real
implementation today, and the trait seam already delivers the
replaceability guarantee.
# Capability traits
Optional features are detected via trait functions and never assumed
by the runtime: `canws`, `cantls`,
`canstream`. A transport that cannot do WebSocket simply reports
`false`; HTTP-only apps keep working.Mongoose.Kernel.canws — Function
canws(transport) → BoolWhether the transport supports WebSocket upgrades. Defaults to false; implementations override it for their capability set.
Mongoose.Kernel.cantls — Function
cantls(transport) → BoolWhether the transport supports TLS. Defaults to false.
Mongoose.Kernel.canstream — Function
canstream(transport) → BoolWhether the transport supports streamed (chunked) responses. Defaults to false.
Mongoose.FakeTransport — Type
FakeTransport — reference transport that runs the pipeline with no FFI.
A `FakeTransport` really is a fake transport: it dispatches requests directly
through the middleware pipeline and router (`process`), bypassing
the C event loop entirely. It declares its capabilities via the ability
traits (`canws`, `cantls`, `canstream`): no WebSocket,
no TLS, streaming supported. It can
drive a full request cycle without a running server — including on systems
where `Mongoose_jll` was never loaded.
The transport is **stateful**: it owns a registry of in-flight streams
(`transport.streams`). A streamed response is bound to exactly one
`FakeStream` — after it is delivered (producer completes) the stream is
`done`, and any further write raises `StreamClosedError` (one response per
stream). `close!(transport)` closes every owned stream (writers become
closed) and rejects new requests, modeling the C transport's close cascade.
# Example
```julia
app = App()
get!(app, "/hello") do req
json((message="Hello World",))
end
client = FakeTransport(app)
resp = client(:get, "/hello")
@assert resp.status == 200
@assert contains(resp.body, "Hello World")
```Mongoose.close! — Function
close!(transport::FakeTransport)Close the transport: every owned stream is closed (its writer becomes closed) and further requests raise an error.
Mongoose.Kernel.validate — Function
validate(req, ::Type{T}; on_error=nothing) → T | ResponseParse and validate the JSON request body into type T. T must be a struct with a keyword constructor or accept a Dict.
Returns the parsed struct, or throws ValidationError if parsing fails. With on_error, a callback (e::ValidationError) → Response is called with the error instead of throwing; the do-block form validate(req, T) do e … end is sugar for on_error=e.
Example
struct CreateUser
name::String
email::String
age::Int
end
post!(app, "/users") do req
user = validate(req, CreateUser) do e
json((error=e.message, field=e.field); status=422)
end
json((id=1, name=user.name))
endMongoose.Kernel.ValidationError — Type
ValidationError — Thrown when request body fails validation.An unhandled ValidationError maps to a 422 Unprocessable Entity response by default at the transport boundary (override with onerror!(app, ValidationError) do req, e … end).
Base integrations
Framework types implement the standard Base protocols where it makes sense: length/isempty on Router and App, ordered dict-like views on Headers (pairs, keys, values, plus getindex, haskey, get, iterate, push!/append!/delete!), == on Headers and Response, and terse one-line show for Router, App, Request, Response, and StreamResponse.
Content Formats
Mongoose.Kernel.Plain — Type
Plain — `text/plain; charset=utf-8` content format.
Built-in format marker types (all subtypes of `AbstractFormat`) are
dispatched by the type system for zero-cost content-type resolution. New
formats are added by subtyping `AbstractFormat` and defining `mime` /
`encode` / `decode`.Mongoose.Kernel.Html — Type
text/html; charset=utf-8 content format. See Plain.
Mongoose.Kernel.Json — Type
application/json; charset=utf-8 content format. See Plain.
Mongoose.Kernel.Css — Type
text/css; charset=utf-8 content format. See Plain.
Mongoose.Kernel.Js — Type
application/javascript; charset=utf-8 content format. See Plain.
Mongoose.Kernel.Xml — Type
application/xml; charset=utf-8 content format. See Plain.
Mongoose.Kernel.Binary — Type
application/octet-stream content format. See Plain.
Mongoose.Kernel.encode — Function
encode(Format, body) → String / Vector{UInt8}Convert body to the wire representation for Format. Extend for custom formats:
Mongoose.encode(::Type{MyFormat}, body) = serialize(body)Mongoose.Kernel.decode — Function
decode(Format, body::String) → AnyParse a request body from its wire representation. Extend for custom formats:
Mongoose.decode(::Type{MyFormat}, body::String) = deserialize(body)Module
Mongoose.Kernel — Module
Kernel — transport-agnostic core of Mongoose.jl.
Contains everything that does NOT depend on a server or an FFI transport:
protocol types (Request, Response, Headers, formats, cookies, WS types,
validation), the pluggable router, the middleware protocol + built-in
middleware, and the URL/string utilities.
This module is self-contained and can be loaded and tested without the C
Mongoose library: `using Mongoose.Kernel`.
The availability of this layer is what makes the transport replaceable —
anything in `Kernel` can be exercised with pure Julia (see
`Kernel.process` for the request→response seam).Errors
Mongoose.Kernel.HTTPError — Type
HTTPError{status} <: ExceptionException that maps to an HTTP error response. The status code is a compile-time constant type parameter, so errorstatus(e) is free and onerror!(app, NotFoundError) (or onerror!(app, HTTPError{404})) registers a handler for exactly that status.
Throw it inside a handler (or middleware) to signal a non-200 reply with a custom message:
get!(app, "/users/:id::Int") do req, id
id in 1:999 || throw(NotFoundError("user $id not found"))
json((id=id,))
endA thrown HTTPError is mapped to Response(status, headers, message) automatically at the transport boundary, unless a more specific onerror! handler (checked first) or a custom onerror!(app, status, …) error page takes precedence.
Named aliases are provided for every standard status from 400 to 511 (RFC 9110 plus the common extensions): BadRequestError, UnauthorizedError, ForbiddenError, NotFoundError, MethodNotAllowedError, RequestHeaderFieldsTooLargeError, PayloadTooLargeError, UnsupportedMediaTypeError, UnprocessableEntityError, TooManyRequestsError, InternalServerError, HTTPVersionNotSupportedError, … One deliberate gap: 501 has no alias because NotImplementedError is a Base type — use HTTPError{501}.
Mongoose.Kernel.errorstatus — Function
errorstatus(e::HTTPError) → IntThe HTTP status code carried by e (the compile-time type parameter).
Mongoose.Kernel.RouteError — Type
RouteError — Route registration or matching failure.Mongoose.Kernel.ServerError — Type
ServerError — Server operation failure (initialization, memory, config).Mongoose.Kernel.BindError — Type
BindError — Failed to bind to address/port.