Skip to content

PGlite

Scrii cod PostgreSQL nativ în unul sau mai multe fișiere .sql. Platforma rulează o instanță PostgreSQL în browser prin intermediul @electric-sql/pglite — fiecare fișier .sql care nu este de test se execută automat la salvare în aceeași bază de date în memorie, în ordine lexicografică.

Proiectul de pornire include un fișier script.sql care creează un tabel greetings, introduce rândul (1, 'Hello, World!') și îl selectează înapoi — extinde-l adăugând mai multă schemă, date de inițializare (seed) și interogări.

Fișierul de intrare

Fișierele soluției se află în rădăcina proiectului:

  • script.sql — fișierul de soluție editabil. Starterul conține un ghid TODO pentru crearea tabelului greetings și selectarea mesajului.
  • Poți redenumi script.sql în orice dorești și poți crea fișiere .sql suplimentare (le poți organiza în submape dacă dorești). Instrumentul de rulare automată descoperă fiecare fișier .sql care nu este de test din structură.

Fișierele rulează în ordine lexicografică după calea completă. Pune un prefix numelor de fișiere (01-schema.sql, 02-data.sql, 03-queries.sql) atunci când ordinea de execuție contează.

Playground-ul pentru baza de date

Panoul din dreapta este o filă live Database care comunică cu aceeași instanță PGlite în memorie:

  • Rezultatul rulării automate — fiecare SELECT (sau altă instrucțiune care produce rânduri) din fișierele tale .sql este redat aici, grupat după fișierul sursă, la fiecare salvare.
  • Tables — o bară laterală cu schema care listează fiecare tabel public, plus coloanele și tipurile sale. Fă clic pe Preview pe un tabel pentru a rula instantaneu SELECT * FROM <table> LIMIT 20 în fila ad-hoc.
  • Ad-hoc query — un editor SQL bazat pe Monaco pentru interogări unice pe baza de date activă. Ctrl/⌘ + Enter rulează interogarea fără a modifica fișierele sursă.

Baza de date este în memorie: reîmprospătarea previzualizării o va șterge. Pentru a curăța baza de date fără a reîncărca, rulează DROP TABLE … în orice fișier sursă sau în fila ad-hoc.

Versiune

Rulează PGlite v0.2.17 (PostgreSQL WASM) cu Vitest v3.2.4 pe Node v22.

Limbaje acceptate

SQL (dialectul PostgreSQL)

Framework de testare

Vitest cu helperul @dojocode/sql-test-helpers.

Memento-uri speciale și detalii de implementare

  • Dialect standard PostgreSQL. Folosește SERIAL / BIGSERIAL pentru ID-uri cu auto-incrementare, ON CONFLICT pentru upsert-uri, RETURNING pentru a obține rândurile introduse.
  • Convertește COUNT(*) la ::int în teste — PGlite returnează în mod implicit bigint (ca șir de caractere), care se compară dificil cu numerele din JS.
  • Fișierele care se potrivesc cu *.test.sql sunt ignorate de rularea automată — rezervat pentru un viitor runner de teste SQL simple (raw-SQL).
  • Testele sunt scrise în TypeScript (*.test.ts) folosind helperul comun createPgliteTestDb(import.meta.url). Helperul pornește o instanță nouă PGlite și încarcă automat fiecare fișier .sql care nu este de test, alături de test, înainte de a rula aserțiunile.
  • PGlite acceptă CTE-uri, funcții fereastră (window functions), JSON, căutare full-text și majoritatea funcționalităților PostgreSQL.

Exemplu cu Vitest:

typescript
import { describe, it, expect, beforeAll } from 'vitest';
import { createPgliteTestDb, type PgliteTestDb } from '@dojocode/sql-test-helpers/pglite';

let db: PgliteTestDb;

beforeAll(async () => {
  db = await createPgliteTestDb(import.meta.url);
});

describe('greetings', () => {
  it("contains the row with message 'Hello, World!'", async () => {
    const rows = await db.query<{ message: string }>(
      'SELECT message FROM greetings WHERE id = 1'
    );
    expect(rows[0]?.message).toBe('Hello, World!');
  });

  it('has exactly one row', async () => {
    const rows = await db.query<{ n: number }>('SELECT COUNT(*)::int AS n FROM greetings');
    expect(rows[0].n).toBe(1);
  });
});

Helperul returnează { query<T>(sql, params?): Promise<T[]>, exec(sql): Promise<void>, raw: PGlite }. query este async iar rândurile sunt returnate deja despachetate (fără a fi nevoie de proprietatea intermediară .rows). Folosește raw doar pentru cazuri avansate (tranzacții, interogări live, listen/notify).

Biblioteci incluse

Cum se face depanarea

Trei moduri de a verifica ce face codul tău SQL:

1. Fila cu rezultatul rulării automate (Auto-run result)

Fiecare rezultat SELECT este redat acolo împreună cu eticheta fișierului sursă. Dacă o interogare nu returnează nimic, secțiunea este omisă — acest lucru în sine este un semnal util că un rând nu a fost introdus sau că un filtru WHERE este prea restrictiv.

2. Fila Tables

Confirmă ce tabele există și tipurile coloanelor acestora în acest moment. Dacă primești erori de tipul "relation does not exist", înseamnă că tabelul nu a fost creat — de obicei din cauza unei erori de sintaxă într-o instrucțiune CREATE de deasupra.

3. Fila Ad-hoc query

Rulează SELECT-uri specifice pe baza de date activă fără a modifica fișierele sursă:

sql
SELECT * FROM information_schema.tables WHERE table_schema = 'public';
SELECT column_name, data_type FROM information_schema.columns WHERE table_name = 'greetings';
SELECT * FROM greetings WHERE id = 1;

Greșeli frecvente

  • Omiterea ON CONFLICT DO NOTHING — rularea repetată a scriptului poate eșua cu erori de încălcare a constrângerilor de unicitate (unique-constraint) dacă o rulare anterioară a introdus deja aceleași rânduri.
  • bigint vs int în testeSELECT COUNT(*) FROM users returnează un șir de caractere; folosește conversia ::int pentru a putea compara cu expect(n).toBe(2).
  • Ordinea de execuție între directoare — fișierele sunt sortate după calea completă, deci a/02-init.sql rulează ÎNAINTE de b/01-init.sql. Pune un prefix și directoarelor atunci când ordinea contează între directoare diferite.