# SolidStart

Serve Courier's proxy from a SolidStart API route and carry the user's session in server code, with @noverax/courier-solid.

`@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

```bash
npx @noverax/create-courier
```

In 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](/docs/create-courier).

## Install

```bash
npm install @noverax/courier-solid @noverax/courier @solidjs/start
```

## Usage

```ts
// src/entry-server.tsx
import { registerSources } from "@noverax/courier"
import { sources } from "~/api"

registerSources(sources)
```

```ts
// src/routes/api/request.ts
import { courierHandler } from "@noverax/courier-solid"

const handler = courierHandler()
export const GET = handler
export const POST = handler
```

`courierHandler()` 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. A read that may be cached arrives as GET, and every other call as POST.

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, given the routes Courier writes into `courier.routes.ts` from the endpoints you mark `.expose()` — see [Exposing Endpoints](/docs/proxy-mode/expose):

```ts
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 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:

```tsx title="src/entry-server.tsx"
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:

```tsx title="src/routes/account.tsx"
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](/docs/server-side/session).

## 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.
