Essentials
Incoming event
The IncomingEvent is the intention, the clean, normalised form of whatever cause arrived. Your handler reads everything it needs from it, and never touches a raw platform request. That is what keeps a handler portable across every context.
#One accessor for input
The principle
A handler that reaches into a platform request object is welded to that platform. A handler that reads named values from an intention is not. The event is that intention: a uniform surface over params, query and body.
In Stone.js
event.get(key, default?) reads a value from any source. The typed overload event.get<T>(key, default) states the shape you expect. event.has(key) tests presence.
create (event: IncomingHttpEvent) {
const title = event.get<string>('title') // body or query or param
const notify = event.get<boolean>('notify', false)
if (!event.has('title')) throw new RuntimeError('title is required')
return this.tasks.add(title, { notify })
}#Nested keys
get reads dotted paths, so you reach into a nested body without unpacking it first. A default guards a missing branch, so deep access never throws on absence.
event.get('user.name', 'Guest') // nested body value, with a fallback
event.get<boolean>('permissions.admin', false)
event.get('payload.items.0.id') // arrays index by number#HTTP details
When you are on an HTTP context, the event exposes typed getters for the request's details. Reach for these when you genuinely need transport specifics; prefer get() for domain values.
| Member | Type | Description |
|---|---|---|
| event.body | unknown | The parsed request body. |
| event.params | Record<string, string> | Captured path parameters. |
| event.query | query params | The parsed query string. |
| event.headers | headers | Request headers. |
| event.cookies | cookies | Parsed cookies (see Cookies). |
| event.uri / path / pathname | string | The request URL and its parts. |
| event.method | HttpMethod | The HTTP verb. |
| event.getHeader(name, def?) | (name) => string | A single request header. |
| event.getCookie(name, def?) | (name) => value | A single cookie. |
| event.isSecure / isXhr / isAjax | boolean | Common request predicates. |
| event.getFile(name) | (name) => UploadedFile | An uploaded file, when the body is multipart. |
| event.clone() | () => IncomingHttpEvent | A copy, for safe experimentation. |
#Content negotiation
When a response should adapt to what the client accepts, ask the event. Each predicate reads the relevant Accept header for you.
if (event.acceptsTypes('json', 'html') === 'json') return data
return renderHtml(data)
const lang = event.acceptsLanguages('en', 'fr') ?? 'en'#Files and uploads
@Post('/import')
import (event: IncomingHttpEvent) {
const file = event.getFile('csv') // one UploadedFile
const images = event.filterFiles(['photos']) // several, by field
return this.importer.run(file)
}#Fingerprinting
event.fingerprint() returns a stable hash of the request's identifying traits, handy as a cache key or a rate-limit bucket without inventing your own scheme.