Baselyra Docs

VS Code extension

The extension talks to a live instance over /admin/v1, /auth/v1 and /storage/v1 — the same API the Studio uses. There is no local index, no cached copy of your database and no service in between. It holds a service key, so read the first section before you connect.

Read this before you connect

What the extension does with it:

  • Stores it in VS Code SecretStorage — the macOS Keychain, libsecret on Linux, the Windows Credential Manager. Never in settings.json, never in a workspace file, never in globalState, and never in a file it writes.
  • Sends it only as an apikey / Authorization header to the instance URL you entered.
  • Never logs it. Every string on its way to the output channel, a notification or an error message passes through a redaction step first — at every log level, including debug.
  • Never puts it in a webview. The SQL results panel receives its data over postMessage after loading, so no server value and no credential is ever part of that page's HTML.
  • Keeps it out of the realtime URL. The WebSocket handshake is made by hand so the credential travels in an Authorization header instead of the ?apikey= query string a browser would be forced to use — a query string ends up in every reverse proxy access log on the way.

There is deliberately no command that reveals the service key. Baselyra: Copy Anon Key copies the anon key, which is public and meant to ship in your frontend.

Install and build

cd vscode
npm install
npm run compile      # or npm run watch
npm test             # the wire-codec and type-mapping checks

Then press F5 to launch an Extension Development Host, or package it with npx @vscode/vsce package. No runtime dependencies; two dev dependencies, @types/vscode and typescript.

Connecting

Run Baselyra: Connect to Instance. You are asked for the origin, shown the warning above, and asked for the service key. Before anything is stored, two checks run:

CheckWhat it proves
GET /healthThe URL is a Baselyra, and its database is up
GET /admin/v1/keysThe key opens the admin API

The anon key passes the first and fails the second with a 401, so pasting the wrong one tells you immediately instead of half-working.

Several named profiles are supported. The status bar shows the active one; click it to switch, add or verify. Baselyra: Disconnect forgets the key but keeps the profile, so reconnecting is one command and one paste.

Database explorer

Schemas grouped the way the server groups them — yours first and expanded, auth and storage collapsed under a lock, baselyra and the catalogs not returned at all. Tables carry a planner row estimate and an RLS indicator: a table with row level security off is flagged in the description and drawn in the warning colour, because that is the one mistake that ships a world-writable API.

Expand a table for its columns with types, primary keys, foreign keys and defaults, and a Policies node listing every policy with its USING and WITH CHECK expressions in the tooltip. Right-click for:

CommandDoes
View RowsOpens the rows as a read-only JSON document
Copy REST URLThe /rest/v1 URL with every column selected. Only public is exposed through REST, so it says so rather than handing you a URL that 404s.
Generate Select StatementA select … from … limit 100 at the cursor of the SQL file you are in, or a new one
Edit PoliciesLists, creates through a guided flow, drops, or opens a definition as an alter policy statement
Enable or Disable Realtime on TableToggles the NOTIFY trigger
Drop TableA modal naming the row count and size, then asks you to type the table's name. CASCADE is a separate button.

SQL

Cmd + Enter in a .sql file runs the selection, or the whole file when nothing is selected. Results open beside the editor in a sortable grid with CSV and JSON export.

A failure becomes a diagnostic on the offending token. Baselyra returns the Postgres position with its errors, and the extension maps that offset back through your selection into a range in the document, so the squiggle lands on the word Postgres actually objected to. The SQLSTATE is the diagnostic's code and the hint is in the message.

Type generation

Baselyra: Generate TypeScript Types reads the live schema and writes a Database type into the workspace, in the shape @baselyra/client expects.

import { createClient } from '@baselyra/client';
import type { Database } from './database.types';

const bl = createClient<Database>(url, ANON_KEY);
const { data } = await bl.from('posts').select('*');   // data: Post[] | null

Row, Insert and Update are emitted per table, with Insert marking a column optional when it has a default, is an identity column, or is nullable. Views get a Row. Only public is emitted, because only public is reachable through /rest/v1.

Set baselyra.generateTypesOnSave to regenerate after saving a .sql file. When the live schema drifts from what was generated, the extension offers to regenerate — once per distinct schema, so declining is not asked again for the same change.

Storage and users

Storage shows buckets with their public/private flag, size limit and MIME allowlist, browsed one prefix at a time the way the server lists them. Upload files from the workspace — the content type is derived from the extension, which is what the bucket's MIME allowlist is checked against — download, delete with a confirmation, and copy a public or signed URL. Asking for a public URL on a private bucket offers a signed one instead of handing you a link that 404s.

Users lists rows of auth.users — the application users of the app you are building — with confirmation state, ban state and last sign-in, searchable by email substring or exact id, paged with an explicit "load more". Copy an id, open the full JSON, or delete with a confirmation.

Realtime inspector

Baselyra: Subscribe to Table picks a table — realtime-enabled ones first — and streams postgres_changes, broadcast and presence frames into the Baselyra Realtime output channel, with a status bar indicator and a live event count. If the table has no NOTIFY trigger it says so and offers to add one, because a subscription to a table without one silently emits nothing.

An optional filter (room_id=eq.42) is offered — a filter is a convenience, not a security boundary. DELETE events are annotated in the log: a deleted row cannot be re-read, so deletes are the one event delivered without an RLS re-check.

Settings

SettingDefaultEffect
baselyra.url""Default URL offered when creating a profile
baselyra.generateTypesOnSavefalseRegenerate types after saving a .sql file
baselyra.typesPathsrc/database.types.tsWhere the Database type is written
baselyra.requestTimeout30000Milliseconds before a request is aborted
baselyra.logLevelinfooff, error, info, debug
baselyra.rowPageSize100Rows fetched when opening a table
baselyra.signedUrlExpiry3600Seconds a signed storage URL stays valid

There is no key setting, on purpose.

Snippets

One snippet file, contributed to TypeScript, JavaScript, Dart and PHP, with prefixes namespaced by dialect so the wrong language never wins a completion.

PrefixFor
bl-client bl-select bl-embed bl-single bl-insert bl-update bl-upsert bl-delete bl-rpc@baselyra/client queries
bl-signin bl-signup bl-magiclink bl-authstate bl-signoutAuth
bl-channel bl-presenceRealtime
bl-upload bl-signedurl bl-publicurl bl-listStorage
bl-aiThe DeepSeek relay
bl-policyThe four policies that make a table per-user private
bl-dart-* · bl-php-*The hand-rolled Dart and PHP clients

What it does not do

  • No ALTER POLICY. /admin/v1 creates and drops policies; it has no update.
  • No table or column editing. Creating a table, adding a column and altering a type are SQL, in the SQL editor. The extension will not grow a schema designer that generates DDL you cannot see.
  • No row editing. Rows open as a read-only JSON document. Writing a row is an update you can read before you run it.
  • No user creation or editing. The Users view lists, searches and deletes.
  • One project per profile, because Baselyra has one project per instance In progress.
  • Realtime broadcast and presence are read-only here. The inspector watches; it does not send.

Failure modes

What you seeWhyFix
401 when verifying a new profileYou pasted the anon key, not the service keyStudio → Settings, or GET /admin/v1/keys
/health passes and the second check failsThe URL is right and the credential is notSame as above
A table is missing from the treeIt is in a hidden schema, or the schema response is staleBaselyra: Refresh
Generated types do not match the APIThe schema changed since generationRegenerate; the extension offers to when it notices
The realtime inspector shows nothingThe table has no trigger, or a proxy ate the Upgrade headerAccept the offer to add the trigger; then the 101 test
A public URL command offers a signed one insteadThat bucket is privateExpected — a public URL would 404

Edit this page Report a problem

Esc
navigate open Esc close