Request & Response

The full req.* and res.* API available inside every handler.

The request object

Property / MethodDescription
req.methodUppercased HTTP method, e.g. "GET"
req.pathPath portion of the URL, no query string
req.originalUrlPath plus query string as received
req.queryStruct of parsed query-string parameters
req.paramsStruct of captured route params (:id segments)
req.bodyParsed request body — populated by boxExpressJSON()/boxExpressUrlencoded()/boxExpressUpload(), empty struct otherwise
req.filesUploaded files, populated by boxExpressUpload()
req.headersStruct of request headers
req.get(name)Read a single header by name — returns null if absent
req.cookiesStruct of parsed cookies
req.ipDirect TCP peer address by default; prefers X-Forwarded-For when app.set("trust proxy", true) — see Configuration
req.protocol / req.secureAlways "http" / false — BoxExpress's own HttpServer never terminates TLS — unless trust proxy is on and the request carries X-Forwarded-Proto: https
req.hostnameThe Host header (or X-Forwarded-Host with trust proxy on) with any :port stripped
req.session / req.sessionIDPopulated by boxExpressSession() — see Sessions
req.rawExchange()Escape hatch to the underlying com.sun.net.httpserver.HttpExchange

The response object

Every terminal method funnels through the same internal path that writes headers and body exactly once and closes the exchange — calling two of them on the same response throws.

MethodDescription
res.send(data)Sends a string body (or delegates to res.json() for structs/arrays)
res.json(data)Serializes and sends with Content-Type: application/json
res.status(code)Sets the status code — chainable
res.sendStatus(code)Sends a status with its reason phrase as the body (e.g. "Not Found")
res.set(name, value) / res.header(name, value)Sets an arbitrary response header — aliases of each other
res.type(mimeType)Sets Content-Type
res.cookie(name, value, options)Sets a Set-Cookie header — options: path, maxAge, httpOnly (default true), secure, sameSite
res.redirect(url, code = 302)Sends a redirect with a caller-supplied status code
res.end(data = "")Ends the response with an optional body, no content-type inference
res.sendBytes(bytes, contentType)Sends raw bytes with an explicit content type, bypassing the string/UTF-8 round trip
res.sendFile(path, options)Sends a file inline, with ETag/Last-Modified 304 support; options.root sandboxes the path against directory-escape
res.download(path, filename)sendFile() that forces Content-Disposition: attachment
res.render(view, data)Renders a view — see Views & Templates
res.dump(data)Sends BoxLang's rich, collapsible dump() HTML view of a variable as the response — useful for quick debugging routes

Examples

bxsapp.get( "/headers-demo", ( req, res ) => {
    res.header( "X-Powered-By", "BoxLang Express" )
       .type( "text/plain" )
       .send( "check the headers" )
} )

app.get( "/whoami", ( req, res ) => {
    res.json( { ip: req.ip, userAgent: req.get( "User-Agent" ) } )
} )

app.get( "/moved", ( req, res ) => {
    res.redirect( "/new-location", 301 )
} )

app.get( "/debug", ( req, res ) => {
    res.dump( req.query )
} )

Tips & Tricks: res.dump()

res.dump() is BoxLang's own rich, interactive dump() — the same one you'd get from a CLI script — rendered as the HTTP response instead of printed to the console. A few things worth knowing:

It's a terminal method — dump one thing

Like res.send()/res.json(), res.dump() writes the response and closes the exchange — nothing can be sent after it. To inspect several things at once, wrap them in a single struct rather than calling it more than once:

bxsapp.get( "/debug", ( req, res ) => {
    res.dump( {
        query: req.query,
        params: req.params,
        headers: req.headers,
        session: req.session
    } )
} )

It's click-to-expand

The rendered dump isn't static markup — struct/array row headers are clickable (and keyboard-focusable) to toggle that branch open or closed, so a large dump doesn't have to be read top-to-bottom. Handy for a deeply nested req.session or a big query result.

Gate debug routes behind your env setting

A dump can expose more than you'd want a stranger to see — internal struct shapes, full session contents, request headers. Guard debug-only routes the same way the framework's own error pages decide whether to reveal a real error message (see Error Handling):

bxsapp.get( "/debug/session", ( req, res, next ) => {
    if ( app.getSetting( "env" ) != "development" ) {
        next() // fall through to the normal 404 — don't reveal the route exists
        return
    }
    res.dump( req.session )
} )

Reach for more control when you need it

res.dump() is a thin convenience wrapper — it only forwards the value to dump, not the underlying dump() BIF's other options. The BIF itself supports several worth knowing about when you need more than the default view:

ArgumentEffect
labelAdds a titled banner above the dump — useful to tell two dumps apart on the same page
expand = falseRenders every branch collapsed by default instead of open — the single biggest win for a large struct or query result
top = nLimits how many levels deep the dump descends before truncating — shrinks the payload for deeply nested data

Since res.dump() doesn't expose these, write the same temp-file round trip it uses internally in your own route when you need them — dump()'s HTML output only ever reaches the process console under BoxLang Express's CLI-based HTTP server, so routing it through a temp file and reading it back is what makes it show up in the response at all:

bxsapp.get( "/debug/session", ( req, res ) => {
    var tmpFile = getTempDirectory() & "dump-" & createUUID() & ".html"
    dump(
        var = req.session,
        format = "html",
        output = tmpFile,
        label = "Session State",
        expand = false,
        top = 3
    )
    var dumpHtml = fileRead( tmpFile )
    fileDelete( tmpFile )
    res.type( "text/html; charset=utf-8" ).send( dumpHtml )
} )

It's fine for dev, not for hot paths

Both the temp-file round trip and the HTML dump renderer itself have real cost compared to res.json(). That's a non-issue for an occasional debug route hit by a developer, but it's not something to leave wired into a frequently-hit production endpoint.