---
title: Browser client
description: sluurp.js, the client every page can import, and what each part of it does.
section: Data
order: 11
---

# Browser client

<p class="lead">Every Sluurp server serves its client, and every app's import map already names it: <code>import { Sluurp } from "sluurp"</code>. Nothing to install. It speaks the <a href="/docs/api">REST API</a> and keeps the sign-in.</p>

```ts title="app.ts"
import { Sluurp } from "sluurp";

const sluurp = new Sluurp();
const todos = sluurp.collection("todos");

const { items } = await todos.list({ filter: "done = false", sort: "-created" });
const todo = await todos.create({ title: "Milk" });
await todos.update(todo.id, { done: true }, { reason: "bought" });
```

`new Sluurp()` talks to the server that served the page. Give it an origin, `new Sluurp("https://school.example.com")`, to talk to another, and `{ project }` to address a project other than the default.

## Collections

`sluurp.collection(name)` has:

| | |
|---|---|
| `list({ page, perPage, sort, filter, asOf })` | A page: `{ items, page, perPage, totalItems, totalPages }` |
| `listAll(options)` | Every page, as one array |
| `getOne(id)`, `getFirst(filter)` | One record |
| `create(data)`, `update(id, data)`, `delete(id)` | Writes. Each takes `{ reason }`, kept with the change |
| `history(id)`, `version(id, seq)`, `restore(id, seq)` | A record's past, for a collection that keeps it |
| `changes(since)` | Every change after one, in order |

## Files

| | |
|---|---|
| `upload(id, field, file)` | Put a `File` or `Blob` in a record's field |
| `createWithFile(data, field, file)` | Make the record and upload in one step |
| `fileUrl(id, field, { w, h, fit, format })` | An address for an `<img>`, resized as asked |
| `srcset(id, field, [400, 800, 1200])` | The `srcset` for the same, at those widths |
| `fileObjectUrl(id, field)`, `fileText(id, field)` | A protected file, fetched with the sign-in |

## Signing in

On the collection that holds people, usually `users`:

```ts title="app.ts"
const users = sluurp.collection("users");
await users.authWithPassword(email, password);

sluurp.authStore.isValid;   // signed in
sluurp.authStore.record;    // who
sluurp.logout();
```

There are also `signUp`, `requestPasswordReset`, `requestSigninLink` (a link by mail), `verifyTwoFactor`, `authRefresh`, and `oauthUrl("google")` for a provider, with `captureOAuthToken(sluurp)` on the page it comes back to. The sign-in is kept in `localStorage`, shared by every page of the site. `sluurp.onAuthFailure` is called when the server stops accepting it.

## What may I do?

```ts title="app.ts"
const may = await sluurp.permissions();
may.can("grades", "update");        // for some rows at least
may.certainly("grades", "delete");  // for every row
```

The collections' own [rules](/docs/rules), asked in advance, to show only the buttons that will work.

## Errors

A failed call throws a `SluurpError` with `status`, `message` (the server's sentence), `body`, and `isAuthError` (401) and `isForbidden` (403) to tell them apart.

## Live

`sluurp.socket({ subscribe: ["messages"] })` opens one WebSocket, with every change to those collections as it happens, read as you. `on(type, listener)` listens; `join(topic)` and `leave(topic)` for presence; `emit(topic, event, data)` says something without keeping it. For a list that stays current by itself, use [Sync](/docs/sync).

## And the rest

`sluurp.conversation(id)` and `sluurp.conversations` for chat, `sluurp.pages` for [Pages](/docs/pages), `sluurp.payments` and `sluurp.billing` for [Payments](/docs/payments), `sluurp.ai` for the models, `sluurp.social` for a feed. And `sluurp.send(path, { method, body, query })` for any endpoint, with the sign-in and the app attached.
