Skip to content

Wire up a real backend

If you confirmed the first run in console-log mode by following 03-first-run.md, this step swaps the same extension over to send data to a real backend. Plan for between thirty minutes and an hour. If you do not need this right now, it is fine to skip.

ModeWho it suitsWhat you need to prepare
supabase-directPeople who would rather not run a backend server themselvesA Supabase project (the free tier is enough), the project URL, and the anon key
server-relayPeople who already run their own server, or are about toThe server URL and an auth token

If this is your first time, supabase-direct is the faster path. Supabase gives you a database, auth, and a REST API on the free tier, so you do not have to stand anything up to start seeing rows arrive.

Sign up at https://supabase.com for free, then click New project. Pick a project name and password. Choose a region close to you (Tokyo or Singapore for most readers in Asia) so latency stays reasonable.

When the project finishes provisioning, open Settings → API in the left-hand menu. Note down the Project URL and the anon public key from that page. You will paste those two values into the extension’s options page in a moment.

2) Create the table that will receive data

Section titled “2) Create the table that will receive data”

In the SQL Editor on the Supabase dashboard, run the following query. It is the smallest table that fits the PageContent shape this base sends.

create table public.page_contents (
id uuid primary key default gen_random_uuid(),
url text not null,
title text,
text_content text,
metadata jsonb,
links jsonb,
images jsonb,
created_at timestamptz default now()
);
-- Allow only INSERT through the anon key (we are assuming private distribution)
alter table public.page_contents enable row level security;
create policy "anon insert"
on public.page_contents for insert
to anon
with check (true);

A quick note on RLS (Row Level Security): it is how Supabase controls who can read and write what. The policy above lets the anon key insert rows but blocks reads, so users of your extension cannot pull each other’s data back out.

3) Connect the extension from its options page

Section titled “3) Connect the extension from its options page”
  1. Open chrome://extensions, find your extension’s card, and click Details.
  2. Click Extension options to open the options page.
  3. Switch the backend mode to supabase-direct and paste in the Project URL and anon key you saved earlier.
  4. Save.

The options page shows three radio buttons at the top (console-log, supabase-direct, server-relay). Picking supabase-direct reveals two input fields below for the Project URL and anon key. Filling both enables the Save button. If the Save button stays disabled, either one of the fields is still empty, or a trailing space or newline came in with the paste.

Open any page in Chrome, click the extension, and hit Analyze and send. Back on the Supabase dashboard, open the Table Editor for page_contents. A new row should be sitting there.

If no row shows up, jump to the “Supabase rows are not arriving” entry in 06-troubleshooting.md.

Your server needs to accept a request shaped like this:

POST /api/page-content
Authorization: Bearer `<token>`
Content-Type: application/json
{
"url": "...",
"title": "...",
"text_content": "...",
"metadata": { ... },
"links": [ ... ],
"images": [ ... ]
}

In the options page, switch the mode to server-relay, paste in the server URL and the token, and save. The extension treats any 2xx response as success.

Switch the mode to console-log in the options page and save — the extension stops calling out and prints to the console again. You can flip back and forth any time. People reach for this often when the backend is temporarily unreachable, or when they want to iterate on a new feature on top of console-log first.

A few of the most common failures are worth listing here.

  • 401 or 403 errors: copy the anon key (or token) again. A trailing space or a stray newline that came in with the paste is the usual culprit.
  • CORS errors: the server has to allow the extension’s origin (chrome-extension://...) in Access-Control-Allow-Origin. This applies only to server-relay. With supabase-direct, supabase-js takes care of it for you.
  • Anything else, the backend section of 06-troubleshooting.md is where to look next.

One step further — does it do what you specified

Section titled “One step further — does it do what you specified”

Once data lands in the backend, the next question is usually: “Does the extension actually do what I originally described as a scenario?”

You can answer that by clicking through the flow yourself, but as the number of scenarios grows, doing it by hand stops being practical. The base ships a tool that handles this for you. From inside Claude Code:

/test-extension

The command reads the §2 “Use scenarios” section of docs/design/extension-spec.md. The one-line scenarios written there get turned into verification code by the AI itself. You wrote the scenario; the AI walks the actual function calls and checks whether reality matches the description.

The first run asks once whether to install Vitest, the test runner. Answering yes installs it and proceeds. The result is not the usual raw test runner output, but a report rewritten in scenario language.

Total scenarios: 5 — 4 passed, 1 failed
✓ Page content extraction — title, body, and labels pulled correctly from a GitHub issue
✓ Backend send — Bearer token + POST sent correctly
✗ PageContent validation — empty title rejection: an empty title was accepted instead of being rejected
File: src/entities/page-content/__tests__/model.test.ts:8
Fix at: model/validate.ts, isValidPageContent

When a failure shows up, telling the same Claude “fix this” is enough. The report already names the file and the function to look at, so no extra context has to be retyped.

For how /test-extension decomposes a scenario into test units, where the test files land on disk, and how it differs from the static review-extension, see the expert verification page.

Once data is landing where you expected, the next step is getting the extension into your users’ hands. 09-distribution.md walks through the trade-offs between zip handoff, Web Store upload, and self-hosted .crx.