DEV Community

Cover image for Why I Put PostgreSQL in WebAssembly to Fix SQL Interview Prep
Rahman
Rahman

Posted on AI-assisted

Why I Put PostgreSQL in WebAssembly to Fix SQL Interview Prep

A few months ago, a backend engineer friend was preparing for a Meta data engineering screen. He asked me to review his SQL prep routine.

He was using the usual suspects: two popular coding websites and a paid course. But watching him practice was painful.

Every single query submission triggered a loading spinner that hung for three to six seconds while a remote backend spun up a container, connected to an ephemeral database, ran the query, and shipped back a JSON blob. Worse, whenever he tried using PostgreSQL-specific functions—like DATE_TRUNC('month', ts) or window functions with specific frame clauses—half the platforms choked. Why? Because under the hood, many "SQL interview" platforms run SQLite with light string-replacement hacks rather than real Postgres.

Day-to-day SQL is usually simple: an ORM query, a basic GROUP BY, or a quick dashboard filter.

FAANG technical interviews are completely different. Companies like Meta, Google, and Amazon test edge-case relational logic: 7-day rolling user retention across discontinuous date gaps, dense ranking ties with multi-column partitions, and recursive hierarchy trees.

Practicing those problems shouldn't feel like waiting on dial-up internet.

I wanted something faster. No servers. No container spin-ups. Real PostgreSQL. Instant grading.

Here is how I built DataCurlew using PostgreSQL 16 compiled to WebAssembly.


The Architecture: Why Run Postgres in the Browser?

Traditionally, an online code execution platform looks like this:

Browser (Monaco/CodeMirror) 
  → API Gateway 
  → Queue (RabbitMQ / Redis) 
  → Sandbox Worker (Docker / Firecracker) 
  → Ephemeral DB 
  → Response (3–5 seconds total)
Enter fullscreen mode Exit fullscreen mode

Running that architecture has two major problems:

  1. Latency: Developers hate feedback delays. When you are debugging a nested CTE, waiting 4 seconds per run destroys your mental flow.
  2. Infrastructure Cost: Hosting thousands of isolated Docker containers executing arbitrary SQL costs real money, which gets passed on to users through steep monthly subscriptions.

Enter PGlite and WebAssembly

ElectricSQL recently open-sourced PGlite—a lightweight build of official PostgreSQL packaged into a WebAssembly binary. It isn't a mock or an emulator. It is the real C source of PostgreSQL compiled via Emscripten down to roughly 3MB gzipped.

That shifted the entire architecture:

Browser Tab (React 18)
  ├── CodeMirror 6 (Editor)
  ├── PGlite WASM (In-Memory PostgreSQL 16 Engine)
  └── Grading Engine (Deterministic Table Diffing)

Total Execution Latency: ~1.4ms
Server Load: Zero
Enter fullscreen mode Exit fullscreen mode

When you hit Run Query on DataCurlew, the query doesn't leave your machine. It executes in-memory inside the browser's WebAssembly runtime.

The query runs in 1.4 milliseconds.

Because it runs real PostgreSQL 16, every single Postgres feature works out of the box:

  • Window functions with custom frame specifications (ROWS BETWEEN 1 PRECEDING AND CURRENT ROW)
  • Common Table Expressions (including WITH RECURSIVE)
  • Full datetime math (INTERVAL '7 days', DATE_TRUNC)
  • Native JSONB manipulation (jsonb_array_elements)
  • Statistical aggregates (PERCENTILE_CONT, FILTER (WHERE ...))

Two Hard Problems We Had to Solve

Moving the database into the client sounds simple until you actually grade user submissions.

1. Fast, Deterministic Sandbox Resets

In an interview, candidates write destructive or state-altering statements (DROP TABLE, DELETE, UPDATE), or they introduce infinite loops in recursive CTEs.

If the user mutates an input table, how do you prevent that mutation from corrupting the hidden test cases?

Instead of re-downloading schemas over the network for every run, we store compiled DDL blueprints in client memory. Before evaluating a candidate's solution against hidden datasets:

  1. The engine spins an isolated in-memory PGlite instance.
  2. Tables are seeded with test fixtures via raw SQL buffers.
  3. The user query executes against that isolated session.
  4. A deterministic diff compares the resulting row matrix against the expected ground-truth dataset.

This entire sequence completes in under 20 milliseconds.

2. Table Comparison is Harder Than It Looks

In standard algorithmic problems, checking output === expected works. In SQL, two queries can be mathematically identical while producing subtle tabular differences:

  • Type Coercion: One query returns 42 as an integer; another returns '42' as text, or 42.00 as a numeric type.
  • Floating-Point Rounding: Financial or retention aggregations can have microscopic rounding variances across WASM platforms.
  • NULL Semantics: In SQL, NULL = NULL evaluates to unknown, not true.
  • Row Ordering: Unless an explicit ORDER BY is specified in the problem statement, relational sets have no guaranteed order.

We built a custom normalization and diffing layer (compareResults.ts) with strict unit test coverage that reconciles column types, evaluates unordered row hashes when order is arbitrary, and checks exact sorting rules when the problem demands strict rank order.


Breaking Down Real FAANG Questions: The Playbooks

Beyond the sandbox, what engineers actually need is strategic intuition.

Most platforms just post the problem and a short solution. But when candidates freeze in an interview, it's almost never because they forgot the syntax of JOIN. It's because they couldn't decompose the problem statement into relational steps.

We built dedicated Company Playbooks for companies like Amazon, Google, and Meta:

-- Example: Meta Interview Question
-- Finding monthly retention with window functions
WITH monthly_activity AS (
  SELECT 
    user_id, 
    DATE_TRUNC('month', stream_date) AS active_month
  FROM streams 
  GROUP BY 1, 2
)
SELECT 
  active_month,
  DENSE_RANK() OVER (ORDER BY COUNT(*) DESC) AS rank
FROM monthly_activity 
GROUP BY active_month;
Enter fullscreen mode Exit fullscreen mode

Each problem breakdown includes:

  • Interactive Multi-Table Schemas: Candidates can toggle between multiple input tables, explore edge-case data fixtures, and see the expected output matrix before typing code.
  • Relational Logic Walkthroughs: Step-by-step breakdowns showing why a CTE or self-join was chosen over a subquery.
  • Immediate Sandbox Handoff: One click opens the problem in the in-browser sandbox with the exact schema pre-loaded.

Takeaways from Shipping WebAssembly to Production

If you are considering compiling heavy C/C++ runtimes into WebAssembly for developer tools:

  1. The browser is an operating system now. Don't default to server-side workers for code execution if a client-side WASM runtime exists. The latency win changes the product feel completely.
  2. Cold start matters. We pre-warm the PGlite instance as soon as the candidate navigates toward a problem. By the time they finish reading the task statement, the engine is warm and responsive.
  3. Keep it accessible. You don't need an account, a credit card, or a Docker daemon running to practice. You just open the page and write SQL.

Check It Out

If you have an upcoming SQL interview—or just want to test how fast PostgreSQL runs inside your browser tab—try it out:

🔗 DataCurlew (datacurlew.com)

💻 Free SQL Learning Path

🏢 Meta SQL Playbook

I would love to hear feedback from other engineers: what SQL edge cases do you find hardest in technical interviews, and what features would make your practice workflow smoother?

Top comments (0)