@noverax/courier-solid serves the proxy that createProxyCaller and createProxyQueryCaller call: one route answers every endpoint, reads over GET and writes over POST, and streams server-sent events.
In one command
npx @noverax/create-courierIn a SolidStart app it adds what this page sets up by hand — the API route that registers your APIs, and Studio's plugin — with a typed example API, its mock, and a page at /courier. See Create Courier.
Install
npm install @noverax/courier-solid @noverax/courier @solidjs/startUsage
// src/entry-server.tsx
import { registerSources } from "@noverax/courier"
import { sources } from "~/api"
registerSources(sources)// src/routes/api/request.ts
import { courierHandler } from "@noverax/courier-solid"
const handler = courierHandler()
export const GET = handler
export const POST = handlercourierHandler() is zero-arg — sources are read at request time from the core global registry populated by registerSources() at server startup. The handler uses Web Standard Request/Response natively. GET requests are routed to query-type endpoints, POST requests handle mutations.
The proxy reads a JSON call up to its maxBodyBytes, 1 MB, and an upload (multipart/form-data) up to its maxUploadBytes, 10 MB; pass others to raise them: courierHandler({ maxUploadBytes: 50 * 1024 * 1024 }).
Solid Start renders the page for a method the route does not export, so PUT, DELETE or OPTIONS on /api/request answer 200 with HTML rather than an error. Courier clients send only GET and POST; export the handler for the other methods too if anything else may call the route, and the proxy answers them with 405. In development, the dev server turns an upstream's 205 Reset Content into a 500.
The client
Pair with createProxyCaller on the client:
import { createProxyCaller } from "@noverax/courier"
import type { api } from "./api"
// The first argument is the key this source has in your `sources` map —
// it is what the proxy addresses, and it travels as `?_s=<name>`.
const caller = createProxyCaller<typeof api>("api", {
proxyPath: { http: "/api/request" },
})
const users = await caller.users.getAll()The user's session
An API that keeps a session gets the user's session with every call a page's query, a server function, an action, an API route or a step it awaits makes. SolidStart keeps the request it is serving itself; register its getRequestEvent once, in the server entry:
import { registerRequestContext } from "@noverax/courier"
import { solidStartRequestContext } from "@noverax/courier-solid"
import { getRequestEvent } from "solid-js/web"
registerRequestContext(solidStartRequestContext(getRequestEvent))The session cookies the API sets reach the browser on the response SolidStart prepares: a page's, a server function's, an API route's, and an error response's too. A page streams, and its headers leave with its first part: a cookie the API sets after that cannot reach the browser, and the log says so. Load what may renew the session with deferStream: true, so the page waits for it:
const getMe = query(me, "me")
export default function Account() {
const user = createAsync(() => getMe(), { deferStream: true })
// …
}The calls the same request makes after it still send the renewed session; only the browser keeps the old one. forward's client address is the one SolidStart reports for the request.
SolidStart 1 and 2 are checked with the Node server their build makes. See User Sessions.
Cancelled calls
When the browser cancels a call — an aborted upload, a query it no longer needs — the proxy stops its own upstream request. The dev server builds the request without a signal that reports a client leaving, so the handler watches the Node response on the event's nativeEvent as well, in development and in production alike.