ApiReturn(label, statusCode, body)
For handlers registered as Express api endpoints (see Cat modes), ApiReturn records the HTTP status and body shape for QA, emits an API_RESPONSE inspector event, and returns a plain object for your handler to pass to Express.
Signature
function ApiReturn<L extends string, T>(
label: L,
statusCode: number,
body: T,
): ApiPayload<T>
interface ApiPayload<T> {
statusCode: number
body: T
}Usage
ApiReturn returns { statusCode, body } so you can wire the same object into Express without duplicating status or payload:
import { ApiReturn, Cat, registerCatPipeline } from '@gloocan/cat-inspector'
import express, { type Request, type Response } from 'express'
class OrderController {
@Cat
create(req: Request, res: Response): void {
const dto = buildOrderDto(req.body)
const r = ApiReturn('CREATED', 201, { ok: true, order: dto })
res.status(r.statusCode).json(r.body)
}
}
const router = express.Router()
registerCatPipeline(router, 'post', '/orders', [
new OrderController().create.bind(new OrderController()),
])Use different labels for each documented response shape (success, auth failure, validation). The QA catalog and AST scan pick up labels from ApiReturn('LABEL', …) in the handler body:
import { ApiReturn, cat, registerCatPipeline } from '@gloocan/cat-inspector'
import express, { type NextFunction, type Request, type Response } from 'express'
const requireSession = cat('Auth.requireSession', function requireSession(
req: Request,
res: Response,
next: NextFunction,
): void {
if (!req.header('x-session')) {
const r = ApiReturn('UNAUTHORIZED', 401, { error: 'missing session' })
res.status(r.statusCode).json(r.body)
return
}
next()
})
const trigger = cat('Workflow.trigger', function trigger(req: Request, res: Response): void {
if (!req.body?.origin) {
const r = ApiReturn('VALIDATION_FAILED', 422, {
issues: [{ path: 'origin', message: 'required' }],
})
res.status(r.statusCode).json(r.body)
return
}
const r = ApiReturn('OK', 200, { ok: true, timeline: buildTimeline(req.body) })
res.status(r.statusCode).json(r.body)
})
const router = express.Router()
registerCatPipeline(router, 'post', '/trigger', [requireSession, trigger])The same ApiReturn('LABEL', status, body) call works inside cat() or catModule handlers registered on a pipeline — labels are discovered from the source for the catalog whether you use @Cat or cat.
For normal RPC handlers (not Express api mode), use Return instead — ApiReturn is only for HTTP pipeline endpoints where the last handler is mode: 'api'.
Error bodies and the QA pipeline
The SDK does not require specific body keys — you choose the JSON shape. The QA pipeline (frontend) looks for two optional conventions on API_RESPONSE bodies and on HTTP step responses so operators can see problems quickly.
error (string)
Use for a single human-readable message (auth failures, business errors, generic failures):
ApiReturn('UNAUTHORIZED', 401, { error: 'missing session' })Pipeline behavior:
- HTTP trace — subtitle includes the string; if status matches the scenario’s expected status but
erroris present, the step may show warn instead of pass. - Inspector event list — red error styling when the body has an
errorkey orstatusCode >= 400; caption appends· <message>frombody.error. - JSON preview — highlights the
"error"key in the response body. - HTTP status assert (expected status mismatch) — prefers
errorwhen building failure detail (error: …).
issues (array)
Use for field-level validation (same item shape the pipeline already understands):
ApiReturn('VALIDATION_FAILED', 422, {
issues: [{ path: 'origin', message: 'required' }],
})Each item should be { path: string, message: string }. The UI summarizes up to three entries as path message; … in the trace subtitle.
Pipeline behavior:
- HTTP trace — subtitle uses the summarized
issuestext (checked aftererrorif both exist). - Inspector event list —
issuesalone does not trigger the red “inspector error” icon; 4xx/5xx status still does. - HTTP status assert — if there is no string
error, failure detail falls back to summarizedissues, then plainmessageon the body.
There is no issue key — use issues (plural).
Quick reference
| Body shape | Trace subtitle | Red error icon in HTTP list | Typical status |
|---|---|---|---|
{ error: "…" } | Yes | Yes (even on 2xx) | 401, 403, 400, … |
{ issues: [{ path, message }] } | Yes | Only if status ≥ 400 | 422 |
| Success payload only | Label + status | No | 200, 201, … |
Suggested patterns
| Scenario | Label example | Status | Body |
|---|---|---|---|
| Auth / permission | UNAUTHORIZED | 401 | { error: "…" } |
| Validation | VALIDATION_FAILED | 422 | { issues: [{ path, message }] } |
| Success | OK, CREATED | 200 / 201 | { ok: true, … } |
You can combine keys or use other fields in the body for your API contract; only error and issues get special treatment in the pipeline UI. Match what your real HTTP clients return so QA runs read the same way production responses do.
Context requirement
ApiReturn uses the active API context (fnKey of the pipeline endpoint). That context is set by the Express integration when the final pipeline handler runs (registerCatPipeline). Outside that context, the call is effectively a no-op for inspector metadata (still returns { statusCode, body }).
Types
Labeled<L, T> is exported for typing branded labels when needed.
See also
registerCatPipeline— wiresApiContextsoApiReturncorrelates to the endpointReturn— labeled returns for RPC handlers- Types —
ApiResponseEntry