@noverax/courier-sveltekit 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 SvelteKit app it adds what this page sets up by hand — the endpoint, the registration in hooks.server.ts, and Studio's plugin — with a typed example API, its mock, and a page at /courier. See Create Courier.
Install
npm install @noverax/courier-sveltekit @noverax/courier @sveltejs/kitUsage
// src/hooks.server.ts
import { registerSources } from "@noverax/courier"
import { sources } from "$lib/api"
registerSources(sources)// src/routes/api/request/+server.ts
import { courierHandler } from "@noverax/courier-sveltekit"
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() in hooks.server.ts. The handler uses Web Standard Request/Response natively. A read that may be cached arrives as GET, and every other call as POST.
In production
SvelteKit answers some calls before the proxy sees them. With @sveltejs/adapter-node:
- Set
ORIGINto the URL the app is served at, such asORIGIN=https://app.example.com node build. Without it SvelteKit cannot tell the protocol it is served over and reports anhttps:request URL, which a browser'shttp:origin never matches, so the proxy refuses everyPOSTwith 403. Behind a proxy that sets forwarding headers,PROTOCOL_HEADERandHOST_HEADERdo the same job. - Raise
BODY_SIZE_LIMIT, which is 512 KB by default and answers any larger body with its own 413Payload Too Large— a 900 KB JSON call or any upload over it — whatever the proxy allows. The proxy reads a JSON call up to itsmaxBodyBytes, 1 MB, and an upload up to itsmaxUploadBytes, 10 MB;BODY_SIZE_LIMIT=11Mcovers both, and a largermaxUploadBytesneeds a larger limit. - Callers without an
Origin: SvelteKit's CSRF check refuses amultipart/form-dataortext/plainPOSTwhoseOrigindoes not match, and one with noOriginat all, such as a server-side script, with a plain-text 403. Browser pages on the app's own origin pass it.
In vite dev, a chunked body larger than the limit resets the connection instead of answering 413, and a client that declares an oversized upload can see the connection close before it has sent it all.
The client
Pair with createProxyCaller on the client, given the routes Courier writes into courier.routes.ts from the endpoints you mark .expose() — see Exposing Endpoints:
import { createProxyCaller } from "@noverax/courier"
import { apiRoutes } from "./courier.routes"
// The routes name the key this source has in your `sources` map —
// it is what the proxy addresses, and it travels as `?_s=<name>`.
const caller = createProxyCaller(apiRoutes, {
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 hook, a load function, a form action or an endpoint makes, once the request being served can be found — and a proxy caller in universal code, rendered on the server, reaches the proxy in process with it:
import { getRequestEvent } from "$app/server"
import { registerRequestContext, registerSources } from "@noverax/courier"
import { svelteKitRequestContext } from "@noverax/courier-sveltekit"
import { sources } from "$lib/api"
registerSources(sources)
registerRequestContext(svelteKitRequestContext(getRequestEvent))import { requestContextHandle } from "@noverax/courier-sveltekit/handle"
export const handle = requestContextHandle()getRequestEvent follows the request through every await where the platform has AsyncLocalStorage, and only until the first one where it has none. The handle hook needs AsyncLocalStorage itself — Node, Bun, Deno, Cloudflare with nodejs_als. Either way the session cookies the API sets go to cookies.set() exactly as the API wrote them, a load function's included. 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. SvelteKit's request signal does not report a client that leaves after its body has arrived, so under @sveltejs/adapter-node the handler watches the client's socket, from platform.req, instead. The Vite dev server exposes no connection: during vite dev a cancelled call still runs to completion upstream.