Primate Logo Primate

Responses

Route handlers return a ResponseLike value that Primate converts into a WHATWG Response. You can return simple values ("implicit" responses) or use explicit handlers from primate/response when you need to control all aspects of the response.

Return value Handler Response Notes
string text 200 text/plain Serve plain text
object json 200 application/json Serve JSON
Blob·File·FileRef binary 200 application/octet-stream Stream contents
URL redirect 302 Redirect to URL
view 200 text/html Serve frontend component
error 404 text/html Show error page
ws 101 WebSocket upgrade
sse 200 text/event-stream Server‑sent events
null 204 new Response(null)
Response as given WHATWG Response

Text

Return strings to serve text/plain.

TypeScript JavaScript Go Python Ruby Grainroutes/text.tsroutes/text.jsroutes/text.goroutes/text.pyroutes/text.rbroutes/text.gr
import route from "primate/route";

route.get(request => "Hello from TypeScript!");

Use the explicit text handler for more options.

TypeScript JavaScript Go Python Ruby Grainroutes/text.tsroutes/text.jsroutes/text.goroutes/text.pyroutes/text.rbroutes/text.gr
import response from "primate/response";
import Status from "primate/response/Status";
import route from "primate/route";

route.post(request => {
  return response.text("Hello from TypeScript!", { status: Status.CREATED });
});

JSON

Return JSON-serializable objects to serve application/json.

TypeScript JavaScript Go Python Ruby Grainroutes/json.tsroutes/json.tsroutes/json.goroutes/json.pyroutes/json.rbroutes/json.gr
import route from "primate/route";

route.get(() => [
  { name: "Donald" },
  { name: "John" },
]);

Use the explicit json handler for more options.

TypeScript JavaScriptroutes/json.tsroutes/json.ts
import response from "primate/response";
import Status from "primate/response/Status";
import route from "primate/route";

route.get(() => response.json([
  { name: "Donald" },
  { name: "John" },
], { status: Status.CREATED }));

Binary

Return Blob, File, ReadableStream or any object exposing { stream(): ReadableStream } to serve application/octet-stream (binary data).

TypeScript JavaScriptroutes/binary.tsroutes/binary.ts
import route from "primate/route";

route.get(() => new Blob(["data"]));

Primate attempts to read the source's name and MIME type if available. Use the explicit binary handler for more options.

TypeScript JavaScriptroutes/binary.tsroutes/binary.ts
import binary from "primate/response/binary";
import route from "primate/route";

route.get(() => binary(new Blob(["data"]), {
  // set filename manually
  headers: { "Content-Disposition": "attachment; filename=data.bin" },
}));

Use rcompat's FileRef to conveniently load a file from disk and stream it out.

TypeScript JavaScriptroutes/binary.tsroutes/binary.ts
import FileRef from "@rcompat/fs/FileRef";
import route from "primate/route";

route.get(() => new FileRef("/tmp/data.bin"));

Redirect

Return a URL to redirect to another address.

TypeScript JavaScriptroutes/redirect.tsroutes/redirect.ts
import route from "primate/route";

route.get(() => new URL("https://example.com/login"));

Use the explicit redirect handler to vary the status or for local redirects.

TypeScript JavaScript Go Python Ruby Grainroutes/redirect.tsroutes/redirect.tsroutes/redirect.goroutes/redirect.pyroutes/redirect.rbroutes/redirect.gr
import Status from "@rcompat/http/Status";
import response from "primate/response";
import route from "primate/route";

route.get(() => response.redirect("https://primate.run", Status.SEE_OTHER));

route.post(request => response.redirect(`/login?next=${request.target}`));

View

Render and serve components from the components directory as text/html.

TypeScript JavaScript Go Python Ruby Grainroutes/index.tsroutes/index.jsroutes/index.goroutes/index.pyroutes/index.rbroutes/index.gr
import response from "primate/response";
import route from "primate/route";

route.get(() => response.view("Counter.jsx"));

Props

Populate the component with initial props.

TypeScript JavaScript Go Python Ruby Grainroutes/index.tsroutes/index.jsroutes/index.goroutes/index.pyroutes/index.rbroutes/index.gr
import response from "primate/response";
import route from "primate/route";

route.get(request => response.view("Counter.jsx", { start: 10 }));

Page

Components are embedded into your app's main HTML page at pages/app.html, with the component code replacing the %body% placeholder. If the app page doesn't exist, Primate falls back to its standard one.

<!doctype html>
<html>
  <head>
    <title>Primate app</title>
    <meta charset="utf-8" />
    %head%
  </head>
  <body>%body%</body>
</html>

Pass a different page option to use another HTML page.

import response from "primate/response";
import route from "primate/route";

route.get(() => response.view("Counter.jsx", { start: 10 },
  { page: "counter.html" })); // render into `pages/counter.html`

Placeholders

You can use placeholders in your HTML pages.

<!doctype html>
<html>

<head>
  <title>%title%</title>
  <meta charset="utf-8" />
  %head%
</head>

<body>%body%</body>

</html>

Populate them in your routes.

import response from "primate/response";
import route from "primate/route";

route.get(() => response.view("Counter.jsx", { start: 10 }, {
  placeholders: {
    title: "Counter",
  },
}));

Partial

Pass a partial: true option to render the component without the enclosing HTML page.

import response from "primate/response";
import route from "primate/route";

// will render Counter without embedding it into pages/app.html
route.get(() => response.view("Counter.jsx", { start: 10 }, { partial: true }));

This is useful for replacing parts of the page whilst retaining the HTML page.

Error

Serve a 404 Not Found error page as text/html.

TypeScript JavaScript Go Python Ruby Grainroutes/not-found.tsroutes/not-found.jsroutes/not-found.goroutes/not-found.pyroutes/not-found.rbroutes/not-found.gr
import response from "primate/response";
import route from "primate/route";

route.get(() => response.error({ body: "Not Found" }));

This handler uses the HTML file at pages/error.html or falls back to a standard one provided by Primate.

<!doctype html>
<html>
  <head>
    <title>Error page</title>
    <meta charset="utf-8" />
    %head%
  </head>
  <body>
    <h1>Error page</h1>
    <p>
      %body%
    </p>
  </body>
</html>

You can pass a custom status to this handler.

TypeScript JavaScript Go Python Ruby Grainroutes/error.tsroutes/error.jsroutes/error.goroutes/error.pyroutes/error.rbroutes/error.gr
import Status from "@rcompat/http/Status";
import response from "primate/response";
import route from "primate/route";

route.get(request => response.error({
  status: Status.INTERNAL_SERVER_ERROR,
}));

As with view, you can pass a different page option to use another HTML page.

import response from "primate/response";
import route from "primate/route";

// use pages/custom-error.html instead of pages/error.html
route.get(() => response.error({ page: "custom-error.html" }));

WebSocket

Upgrade a GET request to ws: and handle open, message, and close events.

import response from "primate/response";
import route from "primate/route";

route.get(() => response.ws({
  open(socket) {
    socket.send("hello");
  },
  message(socket, message) {
    // echo
    socket.send(String(message));
  },
  close(socket) {
    console.log("socket closed");
  },
}));

Server‑sent events

Push out events to the client as text/event-stream.

import response from "primate/response";
import route from "primate/route";

route.get(() => response.sse({
  // connection opened
  open(source) {
    // push event to client
    source.send("open", "hi!");
  },
  // connection closed
  close() { },
}));

Response

Return a custom Response.

import Status from "primate/response/Status";
import route from "primate/route";

route.get(() => new Response("Hi!", {
  status: Status.ACCEPTED,
  headers: { "X-Custom": "1" },
}));

ResponseLike reference

type ResponseLike =
  | string
  | Record<string, unknown>
  | Record<string, unknown>[]
  | Blob
  | ReadableStream
  | URL
  | null
  | Response
  ;
Previous
Requests
Next
Validation