Skip to content

Definitions

The functions that mark a module's default export as something the scanner should pick up. All are exported from clovejs, except the MCP definitions, which come from clovejs/mcp.

Route wrappers

ts
import { get, post, put, patch, del, head, options, all } from "clovejs"
ts
get(handler: RouteHandlerFn): RouteDefinition
FunctionMethod
getGET
postPOST
putPUT
patchPATCH
delDELETE
headHEAD
optionsOPTIONS
allMatches every method

The handler receives (req, res, ctx). Its return value is interpreted by the JSON middleware.

.meta(meta)

Attaches route metadata. Chainable; merges with any previous call.

ts
export default get(handler).meta({ adminOnly: true, json: false })

.cache(policy)

Caches the terminal handler outcome for a GET or HEAD route while the full middleware interceptor chain continues to run on every request.

ts
export default get(handler).cache({
  ttl: "1m",
  staleWhileRevalidate: "5m",
  tags: ["notes"],
  client: { maxAge: "30s" },
})

See Caching for keys, HTTP validators, safety rules and store adapters.

.invalidates(tags)

Invalidates cache tags after the handler and middleware chain complete successfully:

ts
export default post(handler).invalidates(["notes"])

middleware(fn)

ts
middleware(fn: MiddlewareFn): MiddlewareDefinition

fn receives { route, handler, req, res, ctx } and should return handler.execute() unless it means to short-circuit. Files in middlewares/ run in priority order.

service(factory)

ts
service<M>(
  factory: (ctx, hooks) => (M & ThisType<M>) | Promise<M & ThisType<M>>,
): ServiceDefinition<M>

A singleton, created once at boot and exposed as ctx.<filename>. hooks provides onDestroy(fn). M is the resolved service value — the object of methods.

di(spec)

ts
di<T>(spec: {
  lifetime: Lifetime
  value: T | ValueFactory<T>
  eager?: boolean
}): DiDefinition<T>

An injected value, exposed as ctx.<filename>.

FieldTypeMeaning
lifetime"singleton" | "session" | "request"Scope
valueT or (ctx, hooks) => TA plain value, or a factory
eagerbooleanResolve when the scope opens, not on first access

Resolution is lazy by default, so a factory nothing reads never runs. eager turns a request-lifetime value into a per-request and per-delivery hook: start the span in the factory, close it in onDestroy. It requires a factory — eager on a plain value is a boot error.

A value that is a function is treated as a factory. To inject a function as a value, return it from a factory: value: () => myFn.

ws(handler)

ts
ws(handler: (args: WsArgs) => void | Promise<void>): WsDefinition

A WebSocket endpoint. The handler runs once per connection; see WebSockets for the fields on args.

sse(handler)

ts
sse(
  handler: (args: SseArgs) => void | Promise<void>,
): SseRouteDefinition

// SseRouteDefinition extends RouteDefinition with:
.options(options: { heartbeat?: number; retry?: number }): SseRouteDefinition

A Server-Sent Events endpoint. Lives in api/ and runs through the middleware chain like a GET route, but the handler streams events through args and the connection stays open until the client disconnects or close() is called. Stream options are set with a chainable .options(), as routes carry .meta(): heartbeat sends keep-alive comments on an interval; retry sets the initial reconnect hint.

views(engine)

ts
views(engine: ViewEngine): ViewsDefinition

Registers the project's template engine. Lives in views.ts at the source root — one per project. engine.render(template, data, ctx) is the only required member and owns all engine-specific work; it may return a string or Buffer, sync or async. An optional engine.contentType sets the default response type (a res.type() shorthand or full MIME), defaulting to html.

ts
export default views({
  render: (template, data, ctx) => eta.render(template, data),
})

view(template, data?)

ts
view(template: string, data?: unknown): ViewResult

Marks a handler's return value for template rendering. The pipeline hands template and data to the registered engine before it considers JSON, so a handler stays a pure function of its inputs.

ts
export default get(async (req) => view("notes/detail", { id: req.params.id }))

bus(source)

ts
bus(source: MessageBus | BusFactory): BusDefinition

From clovejs/bus. One broker connection, exposed as ctx.bus.<filename>. The source is either a MessageBus object or a (ctx, hooks) factory, branching on typeof exactly like di(). See Message bus.

consume(spec)

ts
// With validation: the payload type is inferred from `input`.
consume<S extends MessageSchema>(spec: {
  bus: BusName
  channel: ChannelSelector
  subscription: string
  input: S
  maxInFlight?: number
  ordered?: "per-key" | "per-partition"
  handler: (payload: InferPayload<S>, ctx, message) => unknown
}): ConsumerDefinition

// Without it: name the payload type.
consume<Payload = unknown>(spec: {
  bus: BusName
  channel: ChannelSelector
  subscription: string
  maxInFlight?: number
  ordered?: "per-key" | "per-partition"
  handler: (payload: Payload, ctx, message) => unknown
}): ConsumerDefinition

From clovejs/bus. One subscription. Unlike routes, nothing is derived from the file path — see why.

FieldMeaning
busWhich bus/ file to bind to. Checked against the generated BusRegistry
channelA literal string, or pattern("orders.#") when the bus advertises patterns. A bare string containing *, # or > is a boot error — use pattern() or literal()
subscriptionThe durable subscriber identity — a queue, a consumer group
inputOptional payload schema. Omit it and use consume<Payload>({...})
maxInFlightConcurrent deliveries in this process. Defaults to 1. A concurrency limit, not an ordering guarantee

Two overloads rather than one, so pass either input or a type argument. Supplying both is a type error: the explicit type argument selects the second overload, which does not accept input. See Validation.

.retry(policy)

ts
.retry({
  attempts: number
  backoff?: { base, factor?, max?, jitter? }
})

Chainable, like .meta(). attempts caps handler failures, including the first — a delivery that never ran the handler to a verdict does not spend one, and bounding those is the broker's job. Boot-checked against the bus's retries capability. See Retries.

reject(reason)

ts
reject(reason: string): RejectSignal

From clovejs/bus. Thrown from a consumer to end a delivery without retrying, in the style of error().

tool(spec)

ts
import { tool } from "clovejs/mcp"

tool(spec: McpToolSpec): McpToolDefinition

An MCP tool — an action a model can invoke. spec takes description (required), and optionally name, title and input. The returned definition has a chainable .meta(), as route definitions do.

input accepts z.object({...}) or the bare { a: z.string() } shape it wraps, and types the handler's first argument from it.

resource(spec)

ts
import { resource } from "clovejs/mcp"

resource(spec: McpResourceSpec): McpResourceDefinition

An MCP resource — data a client reads by URI. spec takes description (required), and optionally uri, name, title and mimeType. Without an explicit uri, one is derived from the file path.

prompt(spec)

ts
import { prompt } from "clovejs/mcp"

prompt(spec: McpPromptSpec): McpPromptDefinition

An MCP prompt — a template the user picks explicitly. Same fields as tool() minus the annotations. Prompt arguments must be z.string(); anything else is a boot error.

error(status, body?)

ts
error(status: number, body?: unknown): HttpError

Creates an error the pipeline renders as a response instead of a 500.

ts
throw error(400, { message: "username and password are required" })

If body is an object with a message property, that value also becomes the Error.message. A string body is used as the message directly. Omitting the body produces { message: "HTTP <status>" }.

isHttpError(value)

ts
isHttpError(value: unknown): value is HttpError

Prefer this over instanceof HttpError. A project can end up with more than one copy of the framework loaded (ESM alongside CJS, or a hoisting miss); this checks a shared symbol brand, so it works across copies.

Also exported

ExportWhat it is
bootstrap, engineEntry points — see Bootstrap
createApp, CloveAppBoot without listening
CloveRequest, CloveResponseThe request and response wrappers
HttpError, CloveBootErrorError classes
MemorySessionStoreThe default in-process session store
createLoggerBuilds the console logger used by default
CloveService<T>, CloveDi<T>Type helpers used by the generated declarations
ScopeUnavailableErrorThrown when an isolated scope is asked for a lifetime it lacks

From clovejs/bus

ExportWhat it is
bus, consume, rejectThe definitions above
pattern(), literal()Declare a channel as a broker-expanded selector, or as a literal containing wildcard characters
memoryBus(options?)In-process bus for dev, tests and single-process deployments. capabilities mirrors the broker you deploy against
readFailures, stampFailures, ATTEMPT_HEADERCarry the failure counter across a retry hop
encodeJson, decodeJson, MessageDecodeErrorThe default wire format, for publish() and a custom decode
matchChannel(selector, channel)The wildcard matcher, for adapters that need one
MessageValidationErrorRaised when a payload fails input

From clovejs/testing

ExportWhat it is

The rest of the testing entrypoint — createTestApp and friends — is covered in Testing.

Released under the MIT License.