tursodatabase / tursodatabase/libsql-client-ts

Queries return non-plain objects (objects with hidden properties)

Open
#302 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
TypeScript
Stars
576
Forks
69
PR merge metrics
No merged PRs in 30d

Description

Hello! 👋

Today I went into a rabbit hole that led me to find out that objects returned by queries in this library are not "plain" JSONs, they contain some hidden (non-enumerable and non-configurable) properties.

The summary is that I can't use objects returned from this library with React Server Components (because those hidden properties cannot be serialized to JSON).

If you want more context, you can check https://github.com/kysely-org/kysely/issues/1394 (I also created an issue in https://github.com/tursodatabase/kysely-libsql/issues/27 for visibility).

Reproduction steps

I took this code from a test and modified it a bit to log the whole object (including all the hidden properties):

import { inspect } from 'node:utils';

// This assumes a client has been instantiated before
await client.batch(
  [
    "DROP TABLE IF EXISTS t",
    "CREATE TABLE t (a)",
     "INSERT INTO t VALUES ('three')",
   ],
   "write",
);
const selectRs = await client.execute({
  sql: "SELECT a FROM t",
})

// These should be the same, but they're not
console.log("original", inspect(selectRs.rows[0], true))
console.log("serialized-deserialized", inspect(JSON.parse(JSON.stringify(selectRs.rows[0])), true))

The output of the console is the following:

original { [0]: 'three', [length]: 1, a: 'three' } // <-- the original object includes these extra hidden properties
serialized-deserialized { a: 'three' }

I dug around the code a bit and I didn't find any obvious reason for these to be used (other than a couple tests that broke and I was able to fix when I removed the code). Let me know if there's something else I might not be looking.

Expected result

I would ideally like the result of every query to be a simple "plain" object.
I know this is technically a breaking change (because people might be using these hidden properties in their code) but I don't think these should be available to the users of this library.

Using the previous example, I would expect to see:

original { a: 'three' } // <-- no extra properties
serialized-deserialized { a: 'three' }

I would like to know if there's any technical reason behind this decision, because this is also affecting how data is retrieved in other libraries (for instance, the particular example I shared using Kysely).

I am also open to work on a PR for this, provided you're okay with me submitting this (mostly given it's a breaking change).

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by running the client.batch and client.execute reproduction and tracing how selectRs.rows[0] is constructed. Check the existing tests referenced by the issue for assumptions about hidden properties. Done means query rows are plain serializable objects, with tests covering the expected shape and any intentional compatibility decision.

Written by the indexing model from the issue text.

Assessment

Tech stack
sqlite, typescript
Domain
api, database
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.