Skip to content

Response Handling

Vitek supports plain object responses (default 200 JSON) and full control via response helpers.

Basic Response (Backward Compatible)

Returning a plain object automatically creates a 200 OK JSON response:

typescript
export default function handler(context: VitekContext) {
  return { message: "Success" }; // Status 200, JSON
}

Custom Status Codes and Headers

Use response helpers for full control over HTTP responses:

typescript
import { created, notFound, json } from "vitek-plugin";

export default function handler(context: VitekContext) {
  // 201 Created
  return created({ id: 123, message: "Resource created" });

  // 404 Not Found
  return notFound({ error: "Resource not found" });

  // Custom status and headers
  return json(
    { data: "custom" },
    {
      status: 201,
      headers: { "X-Custom-Header": "value" },
    }
  );
}

Available Response Helpers

HelperStatusSignature
ok200 OKok(body, headers?)
created201 Createdcreated(body, headers?)
noContent204 No ContentnoContent(headers?)
badRequest400 Bad RequestbadRequest(body?, headers?)
unauthorized401 Unauthorizedunauthorized(body?, headers?)
forbidden403 Forbiddenforbidden(body?, headers?)
notFound404 Not FoundnotFound(body?, headers?)
conflict409 Conflictconflict(body?, headers?)
unprocessableEntity422 Validation ErrorunprocessableEntity(body?, headers?)
tooManyRequests429 Too Many RequeststooManyRequests(body?, headers?)
internalServerError500 Internal Server ErrorinternalServerError(body?, headers?)
redirect301/302/307/308redirect(url, permanent?, preserveMethod?)
jsonCustomjson(body, options?) with status and headers
text200 (or custom)text(body: string, status?) — plain text
html200 (or custom)html(body: string, status?) — HTML

Plain text and HTML

Use text() and html() for non-JSON responses:

typescript
import { text, html } from "vitek-plugin";

// Plain text (Content-Type: text/plain; charset=utf-8)
return text("Hello, world!");
return text("Error", 503);

// HTML (Content-Type: text/html; charset=utf-8)
return html("<h1>Welcome</h1><p>Page content.</p>");
return html("<h1>Not Found</h1>", 404);

Streaming and SSE

You can return a stream as the response body. The handler must return a VitekResponse with body set to a Node.js ReadableStream (e.g. stream.Readable, or the result of Readable.from()). The server will pipe it to the client.

Example: Server-Sent Events (SSE) — set Content-Type: text/event-stream and use a readable stream:

typescript
import { Readable } from "stream";

export default function handler() {
  const stream = new Readable({
    read() {
      this.push("data: " + JSON.stringify({ t: Date.now() }) + "\n\n");
      const t = setTimeout(() => {
        this.push("data: " + JSON.stringify({ t: Date.now() }) + "\n\n");
        this.push(null); // end stream
      }, 1000);
      this.on("close", () => clearTimeout(t));
    },
  });
  return {
    status: 200,
    headers: { "Content-Type": "text/event-stream", "Cache-Control": "no-store" },
    body: stream,
  };
}

For one-off streaming (e.g. a file), you can use Readable.from() or fs.createReadStream() and pass the stream as body.

Cache headers

Use cacheControl and noStore to add Cache-Control headers. Merge the returned object into your response headers:

typescript
import { ok, cacheControl, noStore } from "vitek-plugin";

// Cache for 60 seconds (public)
return { ...ok(data), headers: { ...ok(data).headers, ...cacheControl(60) } };

// With stale-while-revalidate and private
return {
  ...ok(data),
  headers: { ...ok(data).headers, ...cacheControl(60, { staleWhileRevalidate: 120, private: true }) },
};

// Disable caching
return { ...ok(data), headers: { ...ok(data).headers, ...noStore() } };
HelperReturnsDescription
cacheControl(maxAgeSeconds, options?)Record<string, string>Cache-Control: max-age=N. Options: staleWhileRevalidate?: number, private?: boolean.
noStore()Record<string, string>Cache-Control: no-store.

For ETag, set the header manually in your response (e.g. headers: { ...ok(body).headers, 'ETag': '"' + etagValue + '"' }). The framework does not compute ETags automatically.