Send: Structured HTTP Response Utility
A strongly-typed, opinionated API for standardising every JSON response sent from XyPriss — success or error.
Send centralises status code resolution, response body construction, and output flushing into a single interface. Every method produces a JSON object conforming to the IResTemplate contract, guaranteeing predictability for API consumers.
Response Body Contract
Every response produced by Send conforms to IResTemplate:
{
success: boolean;
message: string;
serverName?: string;
data?: unknown;
details: {
error: string;
errorCode: string;
statusCode: number;
};
}200 OK Success
{
"success": true,
"message": "User fetched successfully.",
"serverName": "my-api",
"data": { "id": 1, "name": "Alice" },
"details": { "error": "OK", "errorCode": "SOK", "statusCode": 200 }
}404 Not Found
{
"success": false,
"message": "No user found with id '42'.",
"serverName": "my-api",
"details": { "error": "Not Found", "errorCode": "ENOT", "statusCode": 404 }
}Constructor
new Send(res: XyPrisResponse, configs?: Partial<{
statusCode: Partial<ISeConfigs>;
includeServerName: boolean;
}>)The server name is read from __sys__.vars.__name__ at construction time and remains constant for the lifetime of the instance.
API Reference
2xx — Success
send.ok(message?, data?)
200 OK — successful GET, PUT, PATCH, DELETE with body.
send.ok("User fetched.", { id: 1 });send.created(message?, data?)
201 Created — after persisting a new resource.
send.created("User created.", { id: 42 });send.accepted(message?, data?)
202 Accepted — request received, processing async.
send.accepted("Export started.", { jobId: "abc" });send.noContent()
204 No Content — no body. Complies with RFC 7231.
send.noContent();3xx — Redirection
send.movedPermanently(message?, data?)
301 — clients should update references.
send.movedPermanently("Moved.", { location: "/v2/users" });send.found(message?, data?)
302 — temporary redirect.
send.found("Redirecting.", { location: "/login" });send.notModified()
304 — no body. Use with conditional requests.
send.notModified();4xx — Client Errors
send.badRequest(message?, data?)
400 — malformed requests, validation failures.
send.badRequest("The email field is required.");send.unauthorized(message?, data?)
401 — missing or expired auth credentials.
send.unauthorized("Token expired.");send.forbidden(message?, data?)
403 — authenticated but lacks permissions.
send.forbidden("Admin role required.", { requiredRole: "admin" });send.notFound(message?, data?)
404 — resource does not exist.
send.notFound("Invoice #INV-1042 does not exist.");send.methodNotAllowed(message?, data?)
405 — HTTP method not supported.
send.methodNotAllowed("Only GET allowed.", { allowedMethods: ["GET"] });send.conflict(message?, data?)
409 — request conflicts with current state.
send.conflict("Email already registered.");send.unprocessableEntity(message?, data?)
422 — valid syntax but semantic errors.
send.unprocessableEntity("Birth date must be in the past.");send.tooManyRequest(message?, data?)
429 — rate limit exceeded.
send.tooManyRequest("Rate limit.", { retryAfter: 30 });5xx — Server Errors
send.internalError(message?, data?)
500 — unexpected server failure. Never expose stack traces.
send.internalError("An unexpected error occurred.");send.notImplemented(message?, data?)
501 — functionality not yet supported.
send.notImplemented("PATCH is not supported.");send.badGateway(message?, data?)
502 — invalid response from upstream.
send.badGateway("Provider returned unexpected response.");send.serviceUnavailable(message?, data?)
503 — temporary overload or maintenance.
send.serviceUnavailable("Maintenance until 06:00 UTC.", { retryAfter: "2026-06-01T06:00:00Z" });send.gatewayTimeout(message?, data?)
504 — upstream did not respond in time.
send.gatewayTimeout("Database timed out.", { service: "payments-api", timeoutMs: 5000 });Usage Examples
Basic Setup
Instantiate Send at the beginning of a route handler:
import { Send } from "../utils/Send";
app.get("/users/:id", (req, res) => {
const send = new Send(res);
const user = db.users.findById(req.params.id);
if (!user) {
return send.notFound(`No user found with id '${req.params.id}'.`);
}
send.ok("User fetched successfully.", user);
});Success, Client Error, and Server Error Patterns
const send = new Send(res);
// Success
send.ok("Product retrieved.", product);
send.created("Order placed.", { orderId: "ORD-9821" });
send.accepted("Report started.", { jobId: "JOB-4412" });
send.noContent();
// Client errors
send.badRequest("Quantity must be positive.");
send.unauthorized("Session expired.");
send.conflict("Email already registered.");
send.tooManyRequest("Limit reached.", { retryAfter: 30 });
// Server errors
try {
await processPayment(order);
send.ok("Payment processed.", { transactionId: "TXN-7731" });
} catch (err) {
logger.error(err);
send.internalError("Payment processing failed.");
}Custom Configuration
Override status codes or hide the server name:
const send = new Send(res, {
statusCode: { NOT_FOUND: 404, TOO_MANY_REQUEST: 429 },
includeServerName: false
});
send.ok("Fetched.", payload);
// Response body will not contain `serverName`Design Principles
- Single source of truth for response shape. The
IResTemplatecontract is enforced uniformly across all endpoints. - Explicit over implicit. Each HTTP status has its own named method. No generic
send.status(code, ...)escape hatch. - No body on no-body statuses.
noContent()andnotModified()callres.end()directly, complying with RFC 7231. - Short, stable error codes. The
errorCodefield provides a compact identifier for monitoring dashboards and log parsers. - Defence in depth on 5xx messages. The documentation explicitly reminds developers never to expose raw stack traces in the
messageargument.
Integration with XyPriss
Send is a first-party utility. It depends on XyPrisResponse and the global __sys__ object. It does not introduce any external dependencies and is intended to be instantiated per-request inside route handlers.
import { Send } from "../utils/Send";
import type { XyPrisRequest, XyPrisResponse } from "../server/routing";
export async function getUserById(req: XyPrisRequest, res: XyPrisResponse) {
const send = new Send(res);
// ... handler logic
}Customise unhandled route behaviour with custom status codes and handlers.
