Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .changeset/poor-beds-push.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
"@tanstack/db-collections": patch
"@tanstack/db-example-react-todo": patch
"@tanstack/db": patch
---

- [Breaking change for the Electric Collection]: Use numbers for txid
- misc type fixes
3 changes: 0 additions & 3 deletions docs/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -575,9 +575,6 @@ insert([
{ text: "Buy groceries", completed: false },
{ text: "Walk dog", completed: false },
])

// Insert with custom key
insert({ text: "Buy groceries" }, { key: "grocery-task" })
```

##### `update`
Expand Down
22 changes: 11 additions & 11 deletions examples/react/todo/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -158,31 +158,31 @@ const createTodoCollection = (type: CollectionType) => {
} = transaction.mutations[0].modified
const response = await api.todos.create(modified)

return { txid: String(response.txid) }
return { txid: response.txid }
},
onUpdate: async ({ transaction }) => {
const txids = await Promise.all(
transaction.mutations.map(async (mutation) => {
const { original, changes } = mutation
const response = await api.todos.update(original.id, changes)

return { txid: String(response.txid) }
return response.txid
})
)

return { txid: String(txids[0]!.txid) }
return { txid: txids }
},
onDelete: async ({ transaction }) => {
const txids = await Promise.all(
transaction.mutations.map(async (mutation) => {
const { original } = mutation
const response = await api.todos.delete(original.id)

return { txid: String(response.txid) }
return response.txid
})
)

return { txid: String(txids[0]!.txid) }
return { txid: txids }
},
})
)
Expand Down Expand Up @@ -265,18 +265,18 @@ const createConfigCollection = (type: CollectionType) => {
onInsert: async ({ transaction }) => {
const modified = transaction.mutations[0].modified
const response = await api.config.create(modified)
return { txid: String(response.txid) }
return { txid: response.txid }
},
onUpdate: async ({ transaction }) => {
const txids = await Promise.all(
transaction.mutations.map(async (mutation) => {
const { original, changes } = mutation
const response = await api.config.update(original.id, changes)
return { txid: String(response.txid) }
return response.txid
})
)

return { txid: String(txids[0]) }
return { txid: txids }
},
})
)
Expand All @@ -302,18 +302,18 @@ const createConfigCollection = (type: CollectionType) => {
onInsert: async ({ transaction }) => {
const modified = transaction.mutations[0].modified
const response = await api.config.create(modified)
return { txid: String(response.txid) }
return { txid: response.txid }
},
onUpdate: async ({ transaction }) => {
const txids = await Promise.all(
transaction.mutations.map(async (mutation) => {
const { original, changes } = mutation
const response = await api.config.update(original.id, changes)
return { txid: String(response.txid) }
return response.txid
})
)

return { txid: String(txids[0]) }
return { txid: txids }
},
})
)
Expand Down
23 changes: 14 additions & 9 deletions examples/react/todo/src/api/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
validateUpdateTodo,
} from "../db/validation"
import type { Express } from "express"
import type { Txid } from "@tanstack/db-collections"

// Create Express app
const app: Express = express()
Expand All @@ -23,15 +24,19 @@ app.get(`/api/health`, (req, res) => {
})

// Generate a transaction ID
async function generateTxId(tx: any): Promise<string> {
const result = await tx`SELECT txid_current() as txid`
async function generateTxId(tx: any): Promise<Txid> {
// The ::xid cast strips off the epoch, giving you the raw 32-bit value
// that matches what PostgreSQL sends in logical replication streams
// (and then exposed through Electric which we'll match against
// in the client).
const result = await tx`SELECT pg_current_xact_id()::xid::text as txid`
const txid = result[0]?.txid

if (txid === undefined) {
throw new Error(`Failed to get transaction ID`)
}

return String(txid)
return parseInt(txid, 10)
Copy link
Contributor

Choose a reason for hiding this comment

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

Perhaps good to check that the parsed int is not NaN? Just to safeguard against that even though it doesn't look very plausible.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yeah not sure pg could return an unparsable number? Though I did some more research and txid_current() is actually deprecated. But with the new function, we can cast it to xid then to text and then parse it in js to get the 32bit internal xid that's also on the logical replication:

async function generateTxId(tx: any): Promise<Txid> {
  // The ::xid cast strips off the epoch, giving you the raw 32-bit value
  // that matches what PostgreSQL sends in logical replication streams
  // (and then exposed through Electric which we'll match against
  // in the client).
  const result = await tx`SELECT pg_current_xact_id()::xid::text as txid`
  const txid = result[0]?.txid

  if (txid === undefined) {
    throw new Error(`Failed to get transaction ID`)
  }

  return parseInt(txid, 10)
}

}

// ===== TODOS API =====
Expand Down Expand Up @@ -75,7 +80,7 @@ app.post(`/api/todos`, async (req, res) => {
try {
const todoData = validateInsertTodo(req.body)

let txid!: string
let txid!: Txid
const newTodo = await sql.begin(async (tx) => {
txid = await generateTxId(tx)

Expand All @@ -102,7 +107,7 @@ app.put(`/api/todos/:id`, async (req, res) => {
const { id } = req.params
const todoData = validateUpdateTodo(req.body)

let txid!: string
let txid!: Txid
const updatedTodo = await sql.begin(async (tx) => {
txid = await generateTxId(tx)

Expand Down Expand Up @@ -139,7 +144,7 @@ app.delete(`/api/todos/:id`, async (req, res) => {
try {
const { id } = req.params

let txid!: string
let txid!: Txid
await sql.begin(async (tx) => {
txid = await generateTxId(tx)

Expand Down Expand Up @@ -210,7 +215,7 @@ app.post(`/api/config`, async (req, res) => {
console.log(`POST /api/config`, req.body)
const configData = validateInsertConfig(req.body)

let txid!: string
let txid!: Txid
const newConfig = await sql.begin(async (tx) => {
txid = await generateTxId(tx)

Expand All @@ -237,7 +242,7 @@ app.put(`/api/config/:id`, async (req, res) => {
const { id } = req.params
const configData = validateUpdateConfig(req.body)

let txid!: string
let txid!: Txid
const updatedConfig = await sql.begin(async (tx) => {
txid = await generateTxId(tx)

Expand Down Expand Up @@ -274,7 +279,7 @@ app.delete(`/api/config/:id`, async (req, res) => {
try {
const { id } = req.params

let txid!: string
let txid!: Txid
await sql.begin(async (tx) => {
txid = await generateTxId(tx)

Expand Down
115 changes: 0 additions & 115 deletions examples/react/todo/src/api/write-to-pg.ts

This file was deleted.

6 changes: 4 additions & 2 deletions packages/db-collections/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@
"description": "A collection for (aspirationally) every way of loading your data",
"version": "0.0.21",
"dependencies": {
"@standard-schema/spec": "^1.0.0",
"@tanstack/db": "workspace:*",
"@tanstack/query-core": "^5.75.7",
"@standard-schema/spec": "^1.0.0",
"@tanstack/store": "^0.7.0"
"@tanstack/store": "^0.7.0",
"debug": "^4.4.1"
},
"devDependencies": {
"@electric-sql/client": "1.0.0",
"@types/debug": "^4.1.12",
"@vitest/coverage-istanbul": "^3.0.9"
},
"exports": {
Expand Down
Loading