Transaction blog-post mistakes - in Node.js

I’m trying to implement the solution described here, in Node.js typescript.

There are 2 things that I would like to have clarified:

  1. In the example await session.endSession(); is used, but according to the typings on my machine, endSession() does not return a promise?
  2. The example saves the result of the withTransaction to a variable called transactionResults . But according to my types, withTransaction is void, i.e. does not return anything?
  3. If an error is thrown inside withTransaction, will the catch(e) block not catch it? Reason I ask this question is because according to the example, there is a if block on the variable transactionResults, where it’s concluded that the transaction was intentionally aborted?

Thanks so much if you can help explain the above, and then we can maybe talk about fixing the types :sweat_smile:

I inserted a refrence screenshot here:

Hi @Lauren_Schaefer - sorry for using the mention feature - but would love your answers on the above questions :blush:

Hey Alex,

Happy to hear you’re trying out the tutorial, but it’s a bummer it’s not working for you! I don’t know Typescript, so, unfortunately, I can’t personally offer much help there. Let me reach out to the Node drivers team, and see if we can get some answers on your first two questions.

Here are the docs for endSession: https://mongodb.github.io/node-mongodb-native/3.6/api/ClientSession.html#endSession

Here are the docs for withTransaction: https://mongodb.github.io/node-mongodb-native/3.6/api/ClientSession.html#withTransaction

For question 3 - Let me see if I can describe it in a different way:

    if (transactionResults) {
        console.log("The reservation was successfully created.");
        // This code will be reached if the transaction succeeded
    } else {
        console.log("The transaction was intentionally aborted.");
        // This code will be reached if session.abortTransaction() was called in the transaction
    }
} catch (e) {
    console.log("The transaction was aborted due to an unexpected error: " + e);
    // This code will be reached if something besides an intentional session.abortTransaction() causes the transaction to be aborted
1 Like

Ok, it might be a typings issue, because according to typescript withTransaction is void :flushed:. I actually found this commit in the types repo, where there seems to be a types fix for both the return value and await (question 1 + 2).

Thanks for explaining question 3. So if we do throw new Errror('I was aborted') on the else case in your reply --> all errors will then be handled in the catch?

This is pretty important to me, because when we utilize transactions, I’m mostly interested in knowing if it suceeded or not :face_with_monocle:

Yes, if you throw an Error in the else { }, the catch() { } will catch it. So then any transaction that was intentionally aborted using abortTransaction() or aborted due to an unexpected error will be caught by the catch() {}.

2 Likes

Hi Alex,

Thanks for taking a close look at our transaction tutorial. I see there’s a bit of confusion between the community types and our actual functionality. Luckily the examples from the article are accurate as Lauren has clarified withTransaction returns a command result if the transaction was successful and returns nothing or undefined if the transaction was intentionally aborted.

I’ve created a ticket for us (NODE-3278) to clarify the return types and add some more descriptive notes to these transaction functions in our documentation.

Something to look forward to is our next major release, coming soon, is written in Typescript and we hope that it will offer high quality and accurate type information. It’s not yet production ready yet but you can check out the beta here if that is of interest.

3 Likes

Hi @neal.

I can’t emphasize how important the change to typescript is. We are using a bunch of libraries in our application, and it’s always a big relief when typings are officially supported. It’s the first entry-point of documentation :sunglasses:

Thanks for sharing the links, and our team is looking very much forward to the release written in Typescript with accurate type information :clap:

3 Likes

Just updated to v4 → but I think the typings are still off?

If I don’t give any type arguments to session.withTransaction it still shows void as returnType, but that is not correct(!)

The function does actually return a document with ok, clusterTime etc.

Hey @Alex_Bjorlig, what is the exact version number of the driver you have installed? And have you removed the @types/mongodb package from your project?

In 4.0.0 the type definition was updated to be:

withTransaction<T = void>(fn: WithTransactionCallback<T>, options?: TransactionOptions): ReturnType<typeof fn>;
// and the callback type is:
type WithTransactionCallback<T = void> = (session: ClientSession) => Promise<T>;

So your callback should be forced to return a promise and then the ReturnType<typeof fn> should extract the return type of withTransaction from the function you pass in to withTransaction. Is there an issue with typescript knowing the type that your callback returns potentially?

I use 4.1.0.
I also removed the @types/mongodb.

I think the types still need some work for the withTransaction;

  1. First of all the type argument to withTransaction should not default to void - withTransaction returns something no matter if the type argument was provided.
  2. According to the blog post, there is no requirement for returning anything in the async function provided to withTransaction ==> but that is currently enforced by typescript when providing a type argument.
  3. Is the return type ultimately not wrong, event if providing a types argument? Currently I don’t have time to test it out, but I actually think it returns a document with information about the transaction and clustertime.

Hi @neal - let’s get this thing demystified. Do you have any answers for the 3 points - or do you wan’t me to help do some digging?

/Alex

So currently, the return type of withTransation is something like the following:

{
  ok: 1,
  '$clusterTime': {
    clusterTime: new Timestamp(117, 1630709522),
    signature: {
      hash: new Binary(Buffer.from("0000000000000000000000000000000000000000", "hex"), 0),
      keyId: 0
    }
  },
  operationTime: new Timestamp(116, 1630709522)
}