Skip to content

Instantly share code, notes, and snippets.

@kgoggin
Created Jun 11, 2019
Embed
What would you like to do?
Prisma Client Promise Weridness

The prisma client has what it calls a "fluent" API, which allows you to chain requests for sub-types but work with them all as if they were a promise (docs). Example:

const organization = await prisma.schedule({id: "foo"{}).organization();

All of the examples in the documentation demonstrate using async/await syntax when working with the client. Inspecting what gets returned from a call to the client shows then and catch properties, like a normal promise, as well as the other fields exposed as functions (which is what allows the fluent API).

The Problem

The ReasonML bindings I've written assume normal JavaScript promise behavior - async/await isn't supported in Reason. When encountering a Prisma error, however, my code wasn't actually handling the error like I'd expect - in fact, the catch function wasn't called.

Here's the generated output from the Reason Compiler (Repromise is a 3rd party lib that makes promises easier to work with in Reason. In this case match[0] is a promise getting returned from the function, and match[1] is the resolve function to resolve that promise. Essentially we're creating a new promise and returning it, and resolving it inside the handlers for Prisma's promise.)

function create$2(input) {
  var match = Repromise.make(/* () */ 0);
  var resolve = match[1];
  PrismaClient.prisma
    .schedule(input)
    .then(function(res) {
      return Promise.resolve(
        Curry._1(resolve, /* Ok */ Block.variant("Ok", 0, [res])),
      );
    })
    .catch(function(err) {
      debugger;
      return Promise.resolve(
        Curry._1(resolve, /* Error */ Block.variant("Error", 1, [err])),
      );
    });
  return match[0];
}

This code has an intentional error in it (the input being passed is for creating a schedule, not querying for one) and so Prisma returns an error... but the catch block is never executed, and the debugger statement is never hit.

If I manually edit the compiled output to use async/await, it works as expected:

async function create$2(input) {
  var match = Repromise.make(
  /* () */
  0);
  var resolve = match[1];
  debugger;

  try {
    const res = await PrismaClient.prisma.schedule(input);
    Curry._1(resolve,
    /* Ok */
    Block.variant("Ok", 0, [res]));
  } catch (err) {
    debugger;

    Curry._1(resolve,
    /* Error */
    Block.variant("Error", 1, [err]));
  }

  return match[0]; 
}

In this case, the debugger is triggered and the error is reported correctly.

I'm thinking that something about the way Prisma's client is modifying a base Promise to extend it with the additional fields must somehow be affecting normal Promise behavior?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment