pglite icon indicating copy to clipboard operation
pglite copied to clipboard

Usage with drizzle in React?

Open JanChodorowski opened this issue 10 months ago • 5 comments

const client = await PGlite.create({
  dataDir: "idb://pgdata",
  extensions: { live },
});

const db = drizzle({ client, schema: { usersTable } });

Is it possible to make PGliteProvider accept drizzle and make it work with useLiveQuery? The usePGlite works, the types are wrong though.

JanChodorowski avatar Feb 03 '25 18:02 JanChodorowski

I created my own custom wrapper for useLiveQuery that uses injected drizzle statements. The final hook actually ended up being quite simple!

/**
/* Hook
/**
import { useLiveIncrementalQuery } from "@electric-sql/pglite-react";
import type { Query } from "drizzle-orm";

export const useSubscription = (query: Query, key: string) => {
  const res = useLiveIncrementalQuery(query.sql, query.params, key);

  if (!res) {
    return {
      status: "loading",
    };
  }

  return {
    status: "success",
    data: res.rows,
  };
};
/**
/* Usage
/**
const me = useSubscription(
  db.query.usersTable
    .findFirst({
      where: (users, { eq }) => eq(users.uuid, userUuid),
    })
    .toSQL(),
  "uuid",
);

Hope this helps!

EDIT:

After fooling around a bit more. I managed to keep the drizzle type-inference!

interface LoadingState {
  status: "loading";
}

interface SuccessState<T> {
  status: "success";
  data: T[];
}

type SubscriptionResult<T> = LoadingState | SuccessState<T>;

export const useSubscription = <T>(
  query: PgRelationalQuery<T>,
  key: string,
): SubscriptionResult<T> => {
  const sql = query.toSQL();

  const res = useLiveIncrementalQuery(sql.sql, sql.params, key);
  if (!res) {
    return {
      status: "loading",
    };
  }

  return {
    status: "success",
    data: res.rows as T[],
  };
};

Happy coding!

SemStassen avatar Feb 03 '25 19:02 SemStassen

@SemStassen The hook looks clever! I can't get it all to work though. How do you feed your PGliteProvider? Or are you using db from drizzle directly?

JanChodorowski avatar Feb 03 '25 20:02 JanChodorowski

@JanChodorowski

The db is indeed from drizzle directly.

However I do still expose the PG instance through the PGliteProvider.


export const pg = await PGliteWorker.create(new PGWorker(), {
  extensions: {
    live,
    sync: electricSync(),
  },
});

/**
/* This is the drizzle-orm instance we use in the useSubscription hook!
/**
export const db = drizzle({ client: pg, schema: schema });

export const MasonPGliteProvider = ({
  children,
}: { children: React.ReactNode }) => {
  return <PGliteProvider db={pg}>{children}</PGliteProvider>;
};

Hope this helps!

SemStassen avatar Feb 04 '25 08:02 SemStassen

just chiming in to say there is a really decent lib published for this

https://jsr.io/@makisuo/pglite-drizzle

MiguelsPizza avatar Feb 08 '25 03:02 MiguelsPizza

@JanChodorowski

The db is indeed from drizzle directly.

However I do still expose the PG instance through the PGliteProvider.

export const pg = await PGliteWorker.create(new PGWorker(), { extensions: { live, sync: electricSync(), ...

Do you have or would you be willing to create a repo with a simple hello world version of this example to see how you stitch this all together?

This seems like a very nice and simple way to do things and can probably figure it out from here either way. Thank you

maxwhipw avatar Nov 04 '25 09:11 maxwhipw