Reply Helpers
Send clean, status-discriminated HTTP responses using tree-shakeable reply helpers: json(), ok(), notFound(), and redirect().
Taser.js provides standalone reply helper functions and a unified reply object imported from @taserjs/router/reply to construct standardized, type-safe HTTP responses. Every helper returns a standard Web Response with its HTTP status code embedded into the TypeScript return type (ReplyOf<Status, Body>). Because each helper is an independent standalone function, only the helpers you use are bundled into your application.
JSON Responses (json)
The json() function serializes data into a JSON string and automatically sets the Content-Type: application/json header:
import { json } from "@taserjs/router/reply";
import { t } from "@taserjs/router";
export default t.get("/users").handler(({ ctx }) => {
return json([
{ id: "1", name: "Alice" },
{ id: "2", name: "Bob" },
]);
});You can pass an optional second argument to customize the HTTP status code or set additional response headers:
return json(
{ message: "Created successfully", id: "user_123" },
{
status: 201,
headers: {
"X-Custom-Header": "ProductService",
},
},
);Plain Text Responses (text)
Use text() to return raw strings with Content-Type: text/plain; charset=utf-8:
import { text } from "@taserjs/router/reply";
import { t } from "@taserjs/router";
export default t.get("/robots.txt").handler(() => {
return text("User-agent: *\nDisallow: /admin/\n");
});HTML Responses (html)
Use html() to return HTML documents or markup strings with Content-Type: text/html; charset=utf-8:
import { html } from "@taserjs/router/reply";
import { t } from "@taserjs/router";
export default t.get("/").handler(() => {
return html("<h1>Welcome to Taser.js!</h1>");
});Empty / No Content Responses (noContent)
For successful operations that do not return a payload (such as DELETE endpoints), use noContent(). It sets HTTP status 204:
import { noContent } from "@taserjs/router/reply";
import { t } from "@taserjs/router";
export default t.delete("/items/:id").handler(async ({ ctx, req }) => {
await ctx.db.deleteItem(req.params.id);
return noContent();
});Redirects (redirect)
Use redirect() to send HTTP redirects (default: 302 Found):
import { redirect } from "@taserjs/router/reply";
import { t } from "@taserjs/router";
export default t.get("/old-docs").handler(() => {
// Permanent 301 redirect:
return redirect("/docs/getting-started", { status: 301 });
});External Redirect Protection
By default, redirect() prevents unintended external open redirects. If you need to redirect to external URLs, pass { allowExternal: true }:
return redirect("https://github.com/taserjs/taserjs", { allowExternal: true });Status Code Helpers Reference
All reply helpers are exported from @taserjs/router/reply:
| Helper Function | HTTP Status | Typical Usage |
|---|---|---|
ok(data?, init?) | 200 OK | Generic success response |
created(data?, init?) | 201 Created | Resource created successfully |
accepted(data?, init?) | 202 Accepted | Async job accepted for processing |
noContent(init?) | 204 No Content | Success with empty payload |
redirect(location, init?) | 302 Found | Temporary redirection |
badRequest(data?, init?) | 400 Bad Request | Client query or payload error |
unauthorized(data?, init?) | 401 Unauthorized | Missing or invalid authentication token |
forbidden(data?, init?) | 403 Forbidden | Authenticated user lacks permission |
notFound(data?, init?) | 404 Not Found | Resource or route does not exist |
methodNotAllowed(data?, init?) | 405 Method Not Allowed | HTTP method not supported for this route |
conflict(data?, init?) | 409 Conflict | Duplicate key or concurrency conflict |
payloadTooLarge(data?, init?) | 413 Payload Too Large | Request body exceeds maximum size limit |
unsupportedMediaType(data?, init?) | 415 Unsupported Media Type | Request Content-Type not accepted |
unprocessable(data?, init?) | 422 Unprocessable Entity | Schema validation error |
tooManyRequests(data?, init?) | 429 Too Many Requests | Rate limit or quota exceeded |
internalServerError(data?, init?) | 500 Internal Server Error | Unhandled exception |
Example Usage of Error Helpers
import { forbidden, json, notFound, unauthorized } from "@taserjs/router/reply";
import { t } from "@taserjs/router";
export default t.get("/documents/:id").handler(async ({ ctx, req, state }) => {
const user = state.user;
if (!user) {
return unauthorized({ message: "Login required" });
}
const doc = await ctx.db.findDocument(req.params.id);
if (!doc) {
return notFound({ message: "Document not found" });
}
if (doc.ownerId !== user.id) {
return forbidden({ message: "You do not own this document" });
}
return json(doc);
});Setting Cookies
Prefer the first-party cookie() middleware and { cookies } jar (see Cookie Management). You can also set Set-Cookie manually via response init:
import { json } from "@taserjs/router/reply";
import { t } from "@taserjs/router";
export default t.post("/auth/login").handler(async () => {
const sessionToken = await createSession();
return json(
{ success: true },
{
headers: {
"Set-Cookie": `session=${sessionToken}; HttpOnly; Secure; SameSite=Strict; Path=/`,
},
},
);
});Next Steps
Handling Validation Errors
Understand built-in ValidationError (422) handling and customize envelopes with middleware try/catch — they bypass defineTaser().onError().
Cookie Management
Mount the first-party cookie() middleware to get a Cookie Jar Instance in your handlers. Read, set, sign, and delete HTTP cookies.