Skip to content
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

Fluent API returns incorrect object structure with read replica #12

Open
snake575 opened this issue Sep 16, 2023 · 12 comments · May be fixed by #36
Open

Fluent API returns incorrect object structure with read replica #12

snake575 opened this issue Sep 16, 2023 · 12 comments · May be fixed by #36
Labels
bug Something isn't working

Comments

@snake575
Copy link

When using Prisma's Fluent API with a read replica, the returned object does not adhere to the expected structure.

Repro: read-replicas-demo

Current Behavior:

Using the primary database, the following code:

await prisma.$primary().post.findFirst().author()

returns:

{ id: 1, email: '[email protected]', name: null }

However, when using a read replica, this code:

await prisma.post.findFirst().author() 

returns a nested object:

{ author: { id: 1, email: '[email protected]', name: null } }

Expected Behavior

Using a read replica should also return:

{ id: 1, email: '[email protected]', name: null }
@janpio janpio added the bug Something isn't working label Sep 16, 2023
@casey-chow
Copy link
Contributor

This seems to be a deeper problem in client extensions, since I've encountered this when running my own client extensions.

@zacharyliu
Copy link

This issue seems to be specific to this extension needing to reconstruct a method call to the replica:

const replica = replicaManager.pickReplica()
if (model) {
return replica[model][operation](args)
}
return replica[operation](args)

The fluent API gets translated into operation: 'findFirst', args: { select: { author: true } }, so the reconstructed call has the result incorrectly nested. Normally, the query function seems to handle the work of lifting the nested data.

Within __internalParams, there appears to be a __internalParams.dataPath array which points to the nested path to extract, which maybe could be the solution but is a private API.

@tbell511
Copy link

I am having the same exact issue.

This extension cannot be used on projects using the fluent API.

@tbell511
Copy link

@SevInf do you know when this might get fixed? 🙂

@SevInf
Copy link
Contributor

SevInf commented Oct 25, 2023

Sorry, can not provide ETA at the moment. Query extensions seem to have a problem with Fluent API in general and we probably should fix it in Prisma Client rather than work around in extension.

@janpio
Copy link
Contributor

janpio commented Oct 25, 2023

Do we have a higher level issue for this @SevInf that we can link to?

@daniel-nagy
Copy link

Tried adding a replica and ran into this same issue.

@tbell511
Copy link

Are there any updates to this? This extension is still completely useable for applications that use the fluent API.

@pedrovanzella
Copy link

I'd love to see this fixed too. This is such a great extension, but we can't use it until it supports the fluent API.

@jadenlemmon
Copy link

We are using the fluent API and really want to see this land as well.

FWIW we slightly modified the $allOperations function similar to what @zacharyliu mentioned above that seems to work for our use case. Obviously not ideal.

async $allOperations({
  // @ts-expect-error __internalParams does not appear on the type as it's an internal property
  __internalParams: { dataPath, transaction },
  args,
  model,
  operation,
  query,
}) {
  if (transaction) return query(args);

  if (readOperations.includes(operation)) {
    const replica = replicaManager.pickReplica() as unknown as {
      [key: string]: {
        [key: string]: (args: unknown) => Promise<unknown>;
      };
    };

    if (
      typeof replica === "object" &&
      replica !== null &&
      model &&
      model in replica
    ) {
      const modelMethod = replica[model];

      if (modelMethod && operation in modelMethod) {
        const readMethod = modelMethod[operation];

        if (readMethod) {
          const res = (await readMethod(args)) as {
            [key: string]: unknown;
          };

          // This is a workaround for the Fluent API.
          // This references the internal dataPath to access the correct
          // nested property of the result.
          const path = dataPath as string[];

          if (path && path.length > 1) {
            const p = path[1];
            if (p && p in res) return res[p];
          }

          return res;
        }
      }
    }
  }

  return query(args);
}

@casey-chow
Copy link
Contributor

I put up a PR trying to productionize @jadenlemmon, though of course I'm not able to eliminate the fundamental jankiness of using (more) internal APIs: #36

@baotpham
Copy link

Has anyone found a workaround for this?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.

10 participants