Skip to content

Add TrailBase integration and example #228

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 13 commits into from
Jul 16, 2025
Merged

Conversation

ignatz
Copy link
Contributor

@ignatz ignatz commented Jul 4, 2025

I've also started working on an example, as you suggested, but wanted to send out an RFC early.

Don't hold back. Let me know if this is going roughly the expected direction. If so, also let me know how you'd like to handle a potential example, e.g. should it be part of the same PR, should it be a full replica of the todo example?

Thanks!

Copy link

changeset-bot bot commented Jul 4, 2025

🦋 Changeset detected

Latest commit: fc889b1

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 7 packages
Name Type
@tanstack/trailbase-db-collection Patch
@tanstack/electric-db-collection Patch
@tanstack/query-db-collection Patch
@tanstack/db-example-react-todo Patch
@tanstack/react-db Patch
@tanstack/vue-db Patch
@tanstack/db Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

Copy link
Collaborator

@KyleAMathews KyleAMathews left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking good!

BTW, thinking about this PR made me realize we really should have collection types be in their own packages so e.g. if you want to do a major release you can #235

We'll be migrating the other packages in the next day or two so you can just move this PR to create a new package.

@ignatz
Copy link
Contributor Author

ignatz commented Jul 7, 2025

BTW, thinking about this PR made me realize we really should have collection types be in their own packages so e.g. if you want to do a major release you can #235

Sounds good. I was thinking the same, let alone when adding the backend-specific dependency.

We'll be migrating the other packages in the next day or two so you can just move this PR to create a new package.

If I understood correctly, we'll table this review until the split has happened for me to rebase? Sounds good, just ping this PR and I'll get the train rolling.

Did you think about the example, should we handle it in a follow-up?

Thanks!

@KyleAMathews
Copy link
Collaborator

The refactor is merged so you can now move your code to a trailbase-db-collection package #252

@ignatz
Copy link
Contributor Author

ignatz commented Jul 14, 2025

Awesome! Moved to separate package.

@KyleAMathews
Copy link
Collaborator

Great! I also refactored the example so you can easily add a trail base version. Could you look at doing that? The docker backend perhaps could have a trail base setup too?

@ignatz
Copy link
Contributor Author

ignatz commented Jul 14, 2025

Great! I also refactored the example so you can easily add a trail base version. Could you look at doing that? The docker backend perhaps could have a trail base setup too?

Sure can do. Quick questions:

  • Would you like me to add to the existing example?
  • If so, just add to the existing docker-compose or have a separate compose setup?
  • Would you like to make it part of this initial PR?

@KyleAMathews
Copy link
Collaborator

Yeah same example. It's meant to be an omnibus example so easily see every collection type. And yes, one command to start all of them. And yes in this pr as that'll make it easier to test.

@ignatz
Copy link
Contributor Author

ignatz commented Jul 14, 2025

I added TrailBase to the example. I tried to fumble my way through...

  • Additions and deletions work, however update both for the config and todo fail due to zod validation. I have differnt data types. I already made created_at and updated_at strings, which worked for insertions but not for updates 🤷‍♀️

@KyleAMathews
Copy link
Collaborator

hmm interesting — you're using the zod schemas from drizzle it looks like? So you should use zod schemas consistent with the trailblaze schema? I suppose you can't have Date types w/ sqlite?

@ignatz
Copy link
Contributor Author

ignatz commented Jul 15, 2025

hmm interesting — you're using the zod schemas from drizzle it looks like?

That my understanding, at first I tried to make mine compatible because I didn't necessarily want to change the downstream code.

So you should use zod schemas consistent with the trailblaze schema?

I guess so.

I suppose you can't have Date types w/ sqlite?

SQLite has a DATETIME type, which internally is just an INT64. That said, for type-safety reasons TrailBase requires STRICT mode (see migrations), which doesn't allow DATETIME. W/o STRICT, types are merely affinities and SQLite let's you stick any value into any column :)

@KyleAMathews
Copy link
Collaborator

Ok — yeah the other collections serialize & deserialize to/from Date objects — I imagine most people might want that for Trailblaze? Perhaps you could add a parser & serialize config option to the Trailblaze collection?

@ignatz
Copy link
Contributor Author

ignatz commented Jul 15, 2025

Ok — yeah the other collections serialize & deserialize to/from Date objects — I imagine most people might want that for Trailblaze? Perhaps you could add a parser & serialize config option to the Trailblaze collection?

That's an interesting proposal. I would certainly love not to to diverge the downstream code of the example. Could you elaborate a bit how this could look like, is there any precedent for me to look at? Thanks

EDIT: Would this just be two functions:

parser: (record: Record) -> TItem,
serialize: (item: TItem) -. Record,

? If so, yeah, yeah I'll do that. It will certainly be a good starting point.

@ignatz
Copy link
Contributor Author

ignatz commented Jul 15, 2025

Ok, I found

timestamptz: (date: string) => new Date(date),
, which is probably what you meant. I'm not sure the same approach would work for me, since my base type is just INTEGER as opposed to PG TIMESTAMPZ.

Anyway, it works now 🎉 . Do you think this is a workable starting point?

@KyleAMathews
Copy link
Collaborator

Nice!

Yeah, parsing/serializing the whole row works — perhaps doing it per column would be better though as for big tables, it'd be tedious to have to pass through all the untouched stuff. Electric has a parser by pg type as you found.

@KyleAMathews
Copy link
Collaborator

I tested it briefly — update works if I refresh the page after adding a todo. But not if I add it and then try to check it off. Changing the color of the background doesn't work either.

Screenshot 2025-07-15 at 7 05 22 PM

@ignatz
Copy link
Contributor Author

ignatz commented Jul 16, 2025

perhaps doing it per column would be better though as for big tables, it'd be tedious to have to pass through all the untouched stuff.

I like that, I'm just not sure my TS foo is strong enough. I'm aware that it can render Doom, but how would I define/infer a type Parse as follows:

type TItem = { 
 id: string;
 created: Date;
}
type TRecord = {
 id: string;
 created: number;
}

type Parse = {
 id?: (id: string) => string;
 created: (id: number) => Date;
}

function parse(parse: Parse, record: Partial<TRecord> | Record) : Partial<TItem> | TItem;

Instead I rewrote the serialize/parse implementations to only explicit set the relevant fields. Do you think this could be a workable starting point?

{
  parse: (record: Todo): SelectTodo => ({
    ...record,
    created_at: new Date(record.created_at * 1000),
    updated_at: new Date(record.updated_at * 1000),
  }),
}

I tested it briefly — update works if I refresh the page after adding a todo. But not if I add it and then try to check it off. Changing the color of the background doesn't work either.

My bad. Fixed

TrailBase-TanstackDB.mp4

@KyleAMathews
Copy link
Collaborator

No you have to test it in the same browser.

Screen.Recording.2025-07-16.at.6.39.01.AM.mp4

@KyleAMathews
Copy link
Collaborator

I'm not very good at types either which is why I ask Claude 😆 This is what it came up with:

// Parser type: takes input values and converts them to the target type
type Parser<T, InputType = unknown> = {
  [K in keyof T]?: (value: InputType) => T[K];
};

// Serializer type: takes the typed values and converts them to output format
type Serializer<T, OutputType = string> = {
  [K in keyof T]?: (value: T[K]) => OutputType;
};

@ignatz
Copy link
Contributor Author

ignatz commented Jul 16, 2025

No you have to test it in the same browser.

Did you update and rebuild? I force-pushed an update that should fix your issue. In theory at least :hide:

@KyleAMathews
Copy link
Collaborator

ah oops! I ran git pull but I hadn't setup my checkout correctly and didn't notice I didn't get the latest changes.

It's all working now :-D

Let's get the parser/serializer slimmed down so you only specify what you actually want changed & then I think we're ready to go!

…records and vice-versa on the per-key level.

One downside of this approach is that you always have to specify serialize/parse because I cannot be bothered to make them optional only if no conversions are needed.
@ignatz
Copy link
Contributor Author

ignatz commented Jul 16, 2025

I'm not very good at types either which is why I ask Claude 😆 This is what it came up with:

// Parser type: takes input values and converts them to the target type
type Parser<T, InputType = unknown> = {
  [K in keyof T]?: (value: InputType) => T[K];
};

// Serializer type: takes the typed values and converts them to output format
type Serializer<T, OutputType = string> = {
  [K in keyof T]?: (value: T[K]) => OutputType;
};

I should have been more clear about knowing types well enough that I suspect that it's not trivial. No offense to Claude, would still love to meet them over a beverage and talk about their outlook on humanity 🙃

(There might be a simpler way but that's the easiest I could come up with)

I did do the gymnastics now. It's not pretty. They should be doing what you were asking for but now there's always a parse/serialize parameter needed. Otherwise the gymnastics get even worse. The more positive way to look at it is that previously it wasn't properly type-safe and now it is 🤷‍♀️

@KyleAMathews
Copy link
Collaborator

hmm why can't we make parse/serialize optional?

@ignatz
Copy link
Contributor Author

ignatz commented Jul 16, 2025 via email

@KyleAMathews
Copy link
Collaborator

👍 works for me! Excited to get this out! Finishing up some other stuff but I'll get this merged and released a bit later today 🚀

@ignatz
Copy link
Contributor Author

ignatz commented Jul 16, 2025

👍 works for me! Excited to get this out! Finishing up some other stuff but I'll get this merged and released a bit later today 🚀

Yay 🎉 - It was very fun working with you - looking forward to more.

PS: We can make parse/serialize conditionally optional in the future. It's just just more gymnastics but the change would be api backward compatible. As someone who mostly doesn't read docs, I'm just always happy when I can jump to definition and reasonably reason about the code.

@KyleAMathews
Copy link
Collaborator

It's just just more gymnastics

TypeScript always is haha.

It was fun working with you too! Hopefully TanStack/DB is a super nice addition for your community. I'll tweet it out too — are you / trailbase on the various socials?

@ignatz
Copy link
Contributor Author

ignatz commented Jul 16, 2025 via email

@KyleAMathews
Copy link
Collaborator

Yeah sure push away. Just lemme know when it's ready

@ignatz
Copy link
Contributor Author

ignatz commented Jul 16, 2025

That turned out to be pretty minor 😅 . Feel free to change it as you see fit. All good from my side.

My pristine twitter handle is @trailbase_io (in case that's what you were asking for otherwise no need to mention 🤷‍♀️ ).

Last question, will you take care of publishing the npm package or would you rather have me do that? (not sure if there's some namespacing issue for the package)

Thanks again. This was fun, I'm very excited 🙏 🥳

KyleAMathews and others added 6 commits July 16, 2025 13:07
- Fix Promise resolver implementation in test setup
- Remove unnecessary async keywords from mock functions without await
- Ensure proper Promise return types for mock implementations
- Fix third test function to not be async when no await is used

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
Copy link

pkg-pr-new bot commented Jul 16, 2025

@tanstack/db-example-react-todo

@tanstack/db

npm i https://pkg.pr.new/@tanstack/db@228

@tanstack/electric-db-collection

npm i https://pkg.pr.new/@tanstack/electric-db-collection@228

@tanstack/query-db-collection

npm i https://pkg.pr.new/@tanstack/query-db-collection@228

@tanstack/react-db

npm i https://pkg.pr.new/@tanstack/react-db@228

@tanstack/trailbase-db-collection

npm i https://pkg.pr.new/@tanstack/trailbase-db-collection@228

@tanstack/vue-db

npm i https://pkg.pr.new/@tanstack/vue-db@228

commit: fc889b1

@KyleAMathews KyleAMathews changed the title Add initial TrailBase integration and tests. Add TrailBase integration and tests. Jul 16, 2025
@KyleAMathews KyleAMathews changed the title Add TrailBase integration and tests. Add TrailBase integration and example Jul 16, 2025
@KyleAMathews
Copy link
Collaborator

trailbase-demo.mp4

you need to rebuild your admin w/ db to get realtime updates 😆

@KyleAMathews KyleAMathews merged commit 09c6995 into TanStack:main Jul 16, 2025
4 of 5 checks passed
@github-actions github-actions bot mentioned this pull request Jul 16, 2025
@KyleAMathews
Copy link
Collaborator

https://x.com/kylemathews/status/1945577670550720583

@ignatz
Copy link
Contributor Author

ignatz commented Jul 17, 2025

you need to rebuild your admin w/ db to get realtime updates 😆

You may be joking but the admin refresh does feel incredibly jarring 👀 😅

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants