โ€ข

newsletter

The database is coming to the client

The client is about to regain its authority and independence from the server, which will make writing client apps easier and more enjoyable than ever. The first step is already here: the database is landing on your browser.


Sandro Maglione

Sandro Maglione

Software development

When you thought frontend development couldn't get harder, a new innovation brings the database to the client ๐Ÿ”ฌ

So you now have the burden of the database on you as a frontend dev.

But instead of making your life harder, everything becomes easier ๐Ÿ™Œ

This is how and why ๐Ÿ‘‡


Database on the client? Seriously?

For a while the client has been dominated by the server authority. Data is gold, and the gold was stored on the server, the client just a passive consumer.

Some things came along for the ride:

  • Asynchronous data fetching (loading states, streaming, Suspense)
  • Required error handling
  • Serialization and validation

For a while storing the data on the client was a hassle:

  • Local/Session storage: limited, key-value, no relations, no queries
  • IndexedDB: complex, asynchronous, no queries
  • Cookies: please no ๐Ÿซ 

But not today, today is different. SQL finally landed on the client ๐Ÿช„

WASM

With WASM is possible to build and embed No-JavaScript programs in the browser.

This allows to bring things like postgres inside the client.

File System Access

New Web APIs allow to access the local file system.

You can store and access any file from the local device. What about a SQLite database? That's right ๐Ÿ’ก

PGLite

My current exploration is on PGLite.

PGLite is a WASM build of postgres that runs on the browser ๐Ÿ˜

It works on Node/Bun/Deno and in the browser. You can persist data in memory or on top of IndexDB.

I am using it on my upcoming project on Typeonce alongside Drizzle:

export class Pglite extends Effect.Service<Pglite>()("Pglite", {
  effect: Effect.gen(function* () {
    const indexDb = yield* Config.string("INDEX_DB");

    const client = yield* Effect.tryPromise({
      try: () =>
        _PGlite.PGlite.create(`idb://${indexDb}`, {
          extensions: { live },
        }),
      catch: (error) => new PgliteError({ cause: error }),
    });

    const orm = drizzle({ client });

    const query = <R>(execute: (_: typeof orm) => Promise<R>) =>
      Effect.tryPromise({
        try: () => execute(orm),
        catch: (error) => new PgliteError({ cause: error }),
      });

    return { client, orm, query };
  }),
}) {}

It strips away all the complexity of HTTP requests to the server. And it works offline. And it's fast โšก๏ธ

With the live extension it also allows implementing reactive queries, so you can say goodbye to manual refreshing and most read requests. All with the power and speed of SQL:

export const usePlans = () => {
  const orm = usePgliteDrizzle();
  const query = orm
    .select({
      id: planTable.id,
      calories: planTable.calories,
      fatsRatio: planTable.fatsRatio,
      carbohydratesRatio: planTable.carbohydratesRatio,
      proteinsRatio: planTable.proteinsRatio,
      isCurrent: planTable.isCurrent,
      logs: count(dailyLogTable.date).as("logs"),
    })
    .from(planTable)
    .groupBy(planTable.id)
    .leftJoin(dailyLogTable, eq(planTable.id, dailyLogTable.planId));
  const { params, sql } = query.toSQL();
  return useLiveQuery<PlanWithLogsCount>(sql, params);
};

The major drawback for now is its async API. Keeping it async introduces all the complexity of loading states, error handling and Promises.

LiveStore

Another project I am following is livestore.

It stores SQLite databases inside the local file system, and it gives access to reactive queries as well.

The main selling point for me it's livestore's synchronous API ๐Ÿ‘€

I am planning to write the same project on Typeonce using livestore instead of PGLite, and share with you how I like it.

Great, but why would you do that?

I am full sold on the idea of local-first. It's coming fast, but no fast enough ๐Ÿ™Œ

I see a local-first future, and I want to make it happen as soon as possible.

Part of the advantages of local-first come from shifting the authority to the client. The first step is moving the database ๐Ÿค

Be prepared for more content on the topic, as well as projects showing you how to use it (they will be easy to read since it strips away a lot of the client-server model complexity) ๐Ÿ”œ


Soon about to release the first preview of the local-only calories tracking app I am working on for Typeonce.

Once this lands you will see many new snippets landing on Typeonce, as well as a full project showing you how I did it.

See you next ๐Ÿ‘‹

Start here.

Timeless coding principles, practices, and tools that make a difference, regardless of your language or framework, delivered in your inbox every week.