kysely-libsql icon indicating copy to clipboard operation
kysely-libsql copied to clipboard

Kysely queries return non-plain objects (objects with hidden properties)

Open serchtul opened this issue 9 months ago • 1 comments

Hello! 👋

I just started using Kysely with libSQL for a personal project and ran into an issue while trying to use it with React Server Components using this dialect. Next.js complains whenever you are using the dialect's query results directly (because they have hidden properties).

I raised this as a Kysely issue originally but found out that this is an issue of the dialect, according to one of Kysely's team members: https://github.com/kysely-org/kysely/issues/1394#issuecomment-2731241521

Reproduction Steps

I created a minimal CodeSandbox reproduction of the issue.

The main file looks as follows:

(app/page.tsx)

import ExampleClientComponent from "@/components/client";
import { db } from "./db";
import { inspect } from "node:util";

export default async function Home() {
  const example = await db
    .selectFrom("example")
    .selectAll()
    .executeTakeFirstOrThrow();

  // Some logs to try and understand the issue

  console.log("object is not plain", inspect(example, true, null, true)); // Object has additional hidden properties
  const plain = JSON.parse(JSON.stringify(example)); // This works, but it's cumbersome to do for every query
  console.log("hack for plain object", inspect(plain, true, null, true));

  return (
    <>
      <ExampleClientComponent example={example} />
      {/** This line errors when used with RSC */}

      <ExampleClientComponent example={plain} />
      {/** This workaround works without errors */}
    </>
  );
}

The console result is the following:

...
object is not plain {
  [0]: 'test1',
  [1]: 'this is a test', 
  [length]: 2, // <-- These hidden properties can only be seen when inspecting the object, not during regular debug
  id: 'test1',
  name: 'this is a test'
}

hack for plain object { id: 'test1', name: 'this is a test' }

Only plain objects can be passed to Client Components from Server Components. Classes or other objects with methods are not supported.
  <... example={{id: "test1", name: ...}}>
...

Expected Behavior

Kysely's queries should return plain JSON objects out-of-the-box (without the hidden properties): This would also match the expected behavior when compared to the other (Kysely-maintained) dialects.

import ExampleClientComponent from "@/components/client";
import { db } from "./db";

export default async function Home() {
  const example = await db
    .selectFrom("example")
    .selectAll()
    .executeTakeFirstOrThrow();

  return (
    <>
      <ExampleClientComponent example={example} />
      {/** I would expect this to work as-is, i.e. this object being a JSON (a POJO, if you will) */}
    </>
  );
}

serchtul avatar Mar 18 '25 00:03 serchtul

I fixed this in my fork here

ottomated avatar Apr 08 '25 17:04 ottomated