AffinEQ

Product

Platform overview Integrations Verified, not claimed What isn't built yet

Solutions

Mobile-first startups Agencies building for SMEs Fintechs & regulated teams What people build

Migrate

How a move works From Supabase From Appwrite From Firebase From AWS Amplify From any Postgres Pricing

Developers

Documentation Next.js quickstart JavaScript quickstart Changelog Support Contribute Platform status

Company

Why we exist Customers Blog Partners Contact Sign in Start building
Documentation / Quickstarts

JavaScript quickstart

Read and write a table, and sign a user in by phone. The client is plain TypeScript with no framework dependency, so these steps are the same in React, Vue, Nuxt, SvelteKit, Astro and SolidJS.

1. A project and a table

Create a project in the dashboard and copy its publishable key (it starts aq_pub_), which is safe in a browser or a mobile app. In the SQL editor:

create table todos (
  id bigint generated always as identity primary key,
  user_id uuid not null default auth.uid(),
  title text not null,
  done boolean not null default false
);
alter table todos enable row level security;
create policy "own rows" on todos for all to authenticated
  using (auth.uid() = user_id) with check (auth.uid() = user_id);
grant select, insert, update, delete on todos to authenticated;

2. Install

npm install @affineq/js
import { createClient } from "@affineq/js";

const affineq = createClient("aq_pub_...");

The key names the project, so there is no URL to configure. Works in Node 18+, every modern browser, Deno, Bun and edge runtimes.

3. Read and write

const { data, error } = await affineq.from("todos").select("id, title, done").order("id");

await affineq.from("todos").insert({ title: "buy milk" });
await affineq.from("todos").update({ done: true }).eq("id", 1);
await affineq.from("todos").delete().eq("id", 1);

Until someone is signed in, the policy above returns nothing: that is row-level security doing its job. Every call resolves to { data, error, count, status } and never throws, and an error carries a code, a message and a resolution.

4. Sign a user in

Phone first: add a test number with a fixed code under Authentication → Providers → Phone (for example +254700000001 and 123456), then:

await affineq.auth.signInWithOtp({ phone: "+254700000001" });
const { data, error } = await affineq.auth.verifyOtp({ phone: "+254700000001", token: "123456", type: "sms" });

From then on every from() call carries the user’s token, so the policy returns their rows. The session persists and refreshes itself. For real codes, add your own SMS provider (Mobitech, Africa’s Talking or Twilio) on the same Phone card: you add the key, we handle the rest.

Where next

  • Server-rendered? The Next.js quickstart keeps the session in cookies so a Server Component can read it.
  • Other sign-in methods, storage, live updates and Workers are in the reference.
  • Social sign-in (Google, GitHub, X, LinkedIn and more) is switched on under Authentication → Providers: Authentication.

Type to search. Nothing you type leaves this page.