electric-sql / electric-sql/electric
Feature request: keep table query param first in client-generated /v1/shape URLs for easier debugging
- Dominant language
- TypeScript
- Stars
- 10.4k
- Forks
- 375
- Avg merge
- 3d 1h
- Merged PRs (30d)
- 18
Description
A small quality-of-life request around how the TypeScript client builds `/v1/shape` URLs.
---
## Summary
When using `@electric-sql/client` + `@electric-sql/react`, the client appears to normalize query parameters by sorting them alphabetically (likely via `URLSearchParams.sort()` or similar) before issuing HTTP requests.
When I'm reading logs, including console.error()s I see this:
```text
GET https://localhost:5133/v1/shape?columns=containers_needed_calculated%2Ccontainers_needed_final%2Ccontainers_needed_override%2Cestimate_item_id%2Cid%2Cis_visible_on_work_order%2Clabor_cost_final%2Cmargin_percent%2Cmaterial_cost_calculated%2Cquantity%2Cstatus%2Ctotal_cost_calculated%2Ctotal_price_final%2Cwork_order_id&expired_handle=120433867-1762814934671321&handle=104274672-1762873237440240&log=full&offset=0_0&table=projects_workitem
```
For debugging (especially when scanning logs or devtools for failing requests), it would be much easier if the `table` parameter always appeared first in the query string:
```text
GET https://localhost:5133/v1/shape?table=projects_workitem&columns=...&offset=0_0&log=full&handle=...&expired_handle=...
```
Functionally this shouldn’t change anything on the server side — just the human readability of the URL.
---
## Environment
* `@electric-sql/client`: `^1.1.4`
* `@electric-sql/react`: `^1.0.19`
* `@tanstack/electric-db-collection`: `^0.2.0`
* Browser: Chrome/Brave
* Usage: `useShape` / `getShapeStream` with `params.table` and other query params (`columns`, `offset`, `log`, `handle`, etc.)
---
## What I *think* is happening in the client
From a quick skim of the TypeScript client (and getting gpt-5.1's take TBH) , it looks like there’s a single URL construction path where:
* A `URL` is created for `/v1/shape`
* `params` are written into `url.searchParams`
* Then the query parameters are sorted (probably via `url.searchParams.sort()`) to give a canonical order, which is nice for cache keys and deduplication.
I didn’t want to assume too much about the internal design, but it *looks* like there’s a single helper / method responsible for this normalization, which would make this easy to tweak.
---
## Proposed behavior (one possible approach)
**Goal:** Keep all the benefits of canonicalization, but ensure `table` is always first in the actual request URL.
One possible implementation sketch:
1. Continue to sort parameters as you do today, *then*:
2. If a `table` param exists, rebuild the query string such that:
* `table` is written first,
* all other params follow in whatever canonical order you already use.
GPT-5.1 sugggested pseudocode (just to illustrate the intent):
```ts
function sortSearchParamsWithTableFirst(url: URL, tableKey = 'table') {
const params = url.searchParams
// Whatever canonicalization you do today
params.sort()
const tableValue = params.get(tableKey)
if (!tableValue) return
// Rebuild so `table` is always first
const ordered = new URLSearchParams()
ordered.set(tableKey, tableValue)
for (const [key, value] of params) {
if (key === tableKey) continue
ordered.append(key, value)
}
url.search = ordered.toString()
}
```
Then the URL construction helper would call `sortSearchParamsWithTableFirst(fetchUrl)` instead of (or in addition to) a plain `.sort()`.
### Alternative / more flexible option
If you’d prefer not to bake this in, another option would be to expose a small extension point, for example:
* A config / option on `ShapeStreamOptions` or the client that allows callers to provide a `normalizeUrl(url: URL): URL` or `rewriteSearchParams(params: URLSearchParams): void` callback.
* The default implementation would keep the existing behavior; users like me could plug in a “table first” implementation without changing defaults.
Contributor guide
Assessment
This issue has not been assessed yet.