Astro SDK
Package: @loomup/astro (SDK repository path: packages/astro).
Thin Astro adapter on top of @loomup/client: integration, cookie-backed SSR client, browser islands client, and optional middleware.
Install
# from a monorepo checkout
cd packages/client && npm install && npm run build
cd ../astro && npm install && npm run build
# in your Astro app
npm install ../path/to/packages/astro
# or after publish:
# npm install @loomup/astro @loomup/client
Peer dependency: Astro ≥ 4.
Integration
// astro.config.mjs
import { defineConfig } from "astro/config";
import node from "@astrojs/node";
import loomup from "@loomup/astro";
export default defineConfig({
output: "server", // needed for cookie auth + dynamic data
adapter: node({ mode: "standalone" }),
integrations: [
loomup({
url: process.env.LOOMUP_URL ?? "http://127.0.0.1:3000",
}),
],
});
The integration sets import.meta.env.PUBLIC_LOOMUP_URL so browser code can call createBrowserClient() without an explicit URL.
Server (SSR)
Use in .astro frontmatter, API routes, and middleware. Tokens are stored in httpOnly cookies (loomup-access, loomup-refresh by default).
---
import { createServerClient } from "@loomup/astro/server";
const lb = createServerClient(Astro.cookies, {
url: import.meta.env.LOOMUP_URL ?? import.meta.env.PUBLIC_LOOMUP_URL,
});
// Optional: sign in (writes cookies)
// await lb.auth.signIn({ email, password });
const { data: todos } = await lb.from("todos").select({
limit: 20,
sort: "-id",
});
// Object storage (server [storage] enabled):
// await lb.storage.from("avatars").upload("u/a.png", bytes, { contentType: "image/png" });
// or multipart from an API route:
// import { uploadFromFormData, storageDownloadResponse } from "@loomup/astro/server";
// await uploadFromFormData(lb, "avatars", await Astro.request.formData(), { pathPrefix: "u/" });
---
<ul>
{todos.map((t) => <li>{t.title}</li>)}
</ul>
Auth cookie behavior
| Method | Cookies |
|---|---|
auth.signIn / signUp | Set access + refresh |
auth.refresh (and automatic 401 refresh) | Rotate both |
auth.signOut | Clear both |
Cookie options:
createServerClient(Astro.cookies, {
url: "...",
cookies: {
names: { access: "loomup-access", refresh: "loomup-refresh" },
secure: true, // default true when NODE_ENV=production
path: "/",
accessMaxAge: 3600,
refreshMaxAge: 60 * 60 * 24 * 30,
},
});
Realtime on the server
Do not open WebSocket subscriptions during a request-scoped SSR render. Use REST (select / get / insert / …) on the server; use islands for realtime.
Browser (islands)
// src/components/TodoLive.ts (loaded with client:load)
import { createBrowserClient } from "@loomup/astro/client";
const lb = createBrowserClient(); // uses PUBLIC_LOOMUP_URL
const list = document.querySelector("#todos");
const unsub = lb.from("todos").subscribe((ev) => {
console.log(ev.op, ev.data);
});
// cleanup when navigating away if needed:
// unsub(); lb.closeRealtime();
---
// page
---
<ul id="todos"></ul>
<script>
import { createBrowserClient } from "@loomup/astro/client";
const lb = createBrowserClient();
lb.from("todos").subscribe((ev) => console.log(ev));
</script>
Authenticated islands
httpOnly cookies are not readable from JavaScript. For authenticated realtime in the browser:
- Pass a short-lived access token from the server into the island as a prop (only if you accept it in the HTML payload), or
- Sign in on the client with
createBrowserClient()and keep tokens in memory /localStoragefor that session.
SSR data loads stay authenticated via cookies without exposing tokens to the page.
Middleware (optional)
// src/middleware.ts
import { defineMiddleware } from "astro:middleware";
import { createLoomupMiddleware } from "@loomup/astro/middleware";
const loomupMw = createLoomupMiddleware({
url: import.meta.env.LOOMUP_URL,
loadUser: true, // sets locals.user via auth.me()
});
export const onRequest = defineMiddleware((context, next) =>
loomupMw(context, next),
);
Then in pages:
---
const user = Astro.locals.user;
const lb = Astro.locals.loomup;
---
Add ambient types if you want TypeScript on locals:
// src/env.d.ts
declare namespace App {
interface Locals {
user?: import("@loomup/client").User;
loomup?: import("@loomup/astro/server").ServerLoomupClient;
}
}
Package exports
| Import | Purpose |
|---|---|
@loomup/astro | Default loomup() integration |
@loomup/astro/server | createServerClient, cookie helpers |
@loomup/astro/client | createBrowserClient |
@loomup/astro/middleware | createLoomupMiddleware |
Develop / test this package
cd packages/astro
npm install
npm test # tsc + node:test
See also
- TypeScript SDK — core client API (auth, CRUD, realtime)
- Auth — server token endpoints
- Realtime — WebSocket protocol