Skip to content

Client

Call your Outer server from the browser with @outerjs/sdk, or in-process during SSR with BuiltOuter.client() — same InferRouter types either way.

Updated View as Markdown

Every procedure you register is callable from two places: a browser (or Node) client over HTTP, and an in-process client during SSR. Both share the same router types through InferRouter.

Pick a client

Client Package / API When you use it
HTTP createClient from @outerjs/sdk Browser apps, separate frontends, scripts
In-process BuiltOuter.client(headers?) Server Components, loaders, SSR on the same process

The HTTP client talks to /rpc/** (and /api/auth/** when you enable .auth()). The in-process client skips the network and still sees the caller’s session when you pass request headers.

Create a typed HTTP client

import { createClient } from "@outerjs/sdk";
import type { InferRouter } from "@outerjs/server";
import type { outer } from "./server"; // your BuiltOuter / Outer instance

export const client = createClient<InferRouter<typeof outer>>({
  baseUrl: "http://localhost:3000",
})
  .auth()
  .build();

await client.user.me();
await client.auth.signIn.email({ email, password });

InferRouter<typeof outer> pulls every procedure’s input, output, and errors into the client. Rename a field on the server and the call site turns red.

.auth() on the SDK builder merges a Better Auth client as client.auth. Skip it when your server never called .auth().

Param Type Default Description
baseUrl string Origin of the Outer server
rpcPath `/${string}` "/rpc" Where the oRPC handler is mounted
credentials RequestCredentials platform default ("same-origin") Pass "include" for cross-origin cookie sessions

Call from another origin

When the frontend and Outer run on different origins, pair the SDK with CORS on the server:

// client
createClient<InferRouter<typeof outer>>({
  baseUrl: "https://api.example.com",
  credentials: "include",
})
  .auth()
  .build();

// server
new Outer({
  db: pglite(),
  cors: { origins: ["https://app.example.com"], credentials: true },
});

Without credentials: "include" on the client, the browser never sends the session cookie. Without cors.credentials: true and an explicit origin list on the server, the browser blocks the response. See new Outer() → CORS and Auth → CORS.

Call procedures during SSR

const server = await new Outer({ db: pglite() }).schema(v1_0).auth({ secret }).start();

// In a Server Component / loader — pass the incoming request headers
const api = server.client(request.headers);
const user = await api.user.me();

Pass a Headers object, or a function that returns them per call, so permissions see the same session as an HTTP request. This path never hits /rpc — useful on Next.js and TanStack Start.

Upload files

Pass a File (or Blob) into a procedure that accepts one — the SDK switches the request to multipart/form-data for you:

await client.file.upload({ file: input.files[0] });

See .files().

Realtime

Async generator procedures yield over SSE. Iterate them with for await:

for await (const event of await client.post.onChange()) {
  console.log(event);
}

See Realtime.

Next steps

Navigation

Type to search…

↑↓ navigate↵ selectEsc close