Dealing with unhandled exceptions while passing promises into pg-promise batch transactions

Currently, I am diving into the realm of learning express and pg promise. However, during this journey, I have encountered a puzzling issue that I suspect stems from my somewhat shaky understanding of promises.

Essentially, I have crafted some functions for querying the database. Additionally, I have a function in place to handle any rejections. When used independently, these functions work flawlessly, whether they succeed or encounter an exception.

Let's take a look at a quick sample:

const addToColumn = (tableName, columnName, entryId, amountToAdd) => {
    return db.one('UPDATE ${table:name} SET ${column:name} = ${column:name} + ${amount:csv} WHERE id = ${id:csv} RETURNING *', {
        table: tableName,
        column: columnName,
        amount: amountToAdd,
        id: entryId
    }).catch(err => handleQueryErr(err));
};

Moreover, these functions perform admirably when manually passed into a batch transaction like so:

const transferEnvelopeBudgetByIds =  async (req, res, next) => {
    try{
        req.updatedEnvelopes = await db.tx(t => {
            return t.batch([
                addToColumn('envelopes', 'budget', req.envelopeFromId, -req.transferBudget),
                addToColumn('envelopes', 'budget', req.envelopeToId, req.transferBudget)
            ]);
        }).catch(err => handleTransactionErr(err));
        next();
    }catch(err){
        next(err);
    }
};

However, when attempting to call this function within the middleware instead, if there is any sort of database error (e.g., the database not running), the server crashes with an uncaught exception:

const batchQuery = (queryArray) => {
    return db.tx(t => {
        return t.batch(queryArray);
    }).catch(err => handleTransactionErr(err));
};


const transferEnvelopeBudgetByIds =  async (req, res, next) => {
    try{
        req.updatedEnvelopes = await batchQuery([
            addToColumn('envelopes', 'budget', req.envelopeToId, -req.transferBudget),
            addToColumn('envelopes', 'budget', req.envelopeToId, req.transferBudget)
        ])
        next();
    }catch(err){
        next(err);
    }
};

It appears that the rejection is never caught anywhere and seems to be related to passing an array of promises into the function. The batchQuery function works only when the query succeeds but fails to handle a database error. Any insights on this? If necessary, I can manually write out the entire batchQuery each time I require it, yet I'd like to comprehend why it isn't functioning as expected.

Thank you in advance!

Edit: Here is the updated version with callbacks, which now correctly catches all errors and operates smoothly upon success. Although it identifies errors if any of the queries fail, it unfortunately does not rollback when utilizing the batchQuery function. Originally, I believed that batch would accept an array of promises to resolve, but the current method doesn't align with my expectations.

const transferEnvelopeBudgetByIds =  async (req, res, next) => {
    try{
    req.updatedEnvelopes = await batchQuery(() => 
        [
            addToColumn('envelopes', 'budget', req.envelopeFromId, -req.transferBudget),
            addToColumn('envelopes', 'budget', req.envelopeToId, req.transferBudget)
        ]);  
        next();
    }catch(err){
        next(err);
    }
};

Edit 2: I managed to make it work using functions, though not entirely with the batchQuery function. Now, I include 't' as an optional argument in addToColumn, and it functions superbly.

const addToColumn = (tableName, columnName, entryId, amountToAdd, t) => {
    let context = t;
    if(context === undefined){
        context = db;
    }
    return context.one('UPDATE ${table:name} SET ${column:name} = ${column:name} + ${amount:csv} WHERE id = ${id:csv} RETURNING *', {
        table: tableName,
        column: columnName,
        amount: amountToAdd,
        id: entryId
    }).catch(err => handleQueryErr(err));
};

const transferEnvelopeBudgetByIds = async (req, res, next) => {
    try{
        req.updatedEnvelopes = await db.tx(t => {
            return t.batch([
                addToColumn('envelopes', 'budget', req.envelopeFromId, -req.transferBudget, t),
                addToColumn('envelopes', 'budget', req.envelopeToId, req.transferBudget, t)
            ])
        }).catch(err => handleTransactionErr(err));
        next();
    }catch(err){
        next(err);
    }
};

Answer №1

Your error-handling approach is scattered, leading to unpredictable transaction outcomes. To improve consistency and reliability, consider consolidating error handling into a single cohesive structure...

function addToColumn(tableName, columnName, entryId, amountToAdd, t) {
    return (t || db).one('UPDATE ${table:name} SET ${column:name} = ${column:name} + ${amount:csv} WHERE id = ${id:csv} RETURNING *', {
        table: tableName,
        column: columnName,
        amount: amountToAdd,
        id: entryId
    });
};

async function transferEnvelopeBudgetByIds(req, res, next) {
    try {
        const result = await db.tx(async t => {
            const one = await addToColumn('envelopes', 'budget', req.envelopeFromId, -req.transferBudget, t);
            const two = await addToColumn('envelopes', 'budget', req.envelopeToId, req.transferBudget, t);
            return {one, two};
        });
        // result = {one, two};
        next();
    } catch(err) {
        next(err);
    }
};

Furthermore, the use of batch is now considered obsolete and unnecessary for your current needs.

Answer №2

This code snippet aims to prevent the server from crashing when encountering an uncaught exception

const updatingBudgetByIds = async (req, res, next) => {
try {
    req.data = await executeQuery([
        addToColumn('budget_data', 'amount', 200, req.toId).catch(err => handleExecuteError(err)),
        addToColumn('budget_data', 'amount', 200, req.fromId).catch(err => handleExecuteError(err))
    ])
    next();
} catch (err) {
    next(err);
}};

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Differences between async/await and then methods in React, demonstration shows that only async/await is functional. Why is

Trying to understand and implement two different API data fetching methods in React app development. The first method involves using the JavaScript Fetch API, while the second method utilizes the async/await syntax. I am currently experimenting with both a ...

Shake up your background with a random twist

Seeking assistance with customizing the Aerial template from HTML5UP. I am interested in selecting a scrolling background randomly from a pool of images. Can someone guide me on how to achieve this? Presently, the background is defined within a code block ...

Removing the switcher outline in Bootstrap Switch: a step-by-step guide

I have implemented the bootstrap-switch - v3.3.1, however, I want to remove the default blue outline that appears around the switcher when toggling it on or off. Despite setting style="outline: 0 none; for the input, the outline remains visible. Below is ...

Can I deactivate JavaScript on my website directly from my server settings?

I'm currently attempting to link my Android application to a PHP script hosted on a free server. However, when my app tries to access the page, I receive an HTML message stating that JavaScript is disabled and needs to be enabled in order to view the ...

Is there a way to enable data-add-back-btn using jQuery script?

Currently, I am utilizing jQuery 1.9.1 and jQuery Mobile 1.3.1 to define multiple pages within my project setup: <div id="q1" data-role="page" data-add-back-btn="false" data-back-btn-text="Home"> <div data-role="header" data-position="fixed" ...

Tips for making a sidebar sticky when scrolling

In order to keep the right-side bar fixed, I have implemented this javaScript function: <script type="text/javascript> $(document).ready(function () { var top = $('#rightsidebar-wrapper').offset().top - parseFloat($('#rightsideb ...

Issue with PG npm package's exception handling functionality is not functioning as expected

After installing "pg": "^8.0.2" and setting up the database.js file with database credentials, I noticed that no matter the issue, it never seems to enter the catch block to display errors. Instead, it always logs connected to the database. Can anyone help ...

Solving Shopify public app session conflicts: Tips and tricks

Recently, I developed a basic embedded public app on Shopify using Node.js for testing purposes. The app functions as expected but I encountered an issue with session clash when opening two stores in the same browser but in different tabs. For instance, ...

When attempting to use JQuery autocomplete, the loading process continues indefinitely without successfully triggering the intended function

Currently, I am utilizing JQuery autocomplete to invoke a PHP function via AJAX. Below is the code snippet I am working with: $("#client").autocomplete("get_course_list.php", { width: 260, matchContains: true, selectFirst: false }); Upon execution, ...

Having difficulty sending variables to onreadystatechange

Here is the latest version of the source code: var xhttp = new XMLHttpRequest(); function passVars(var1, var2, var3) { if (var1.readyState == 4) { if (var1.status == 200) { var data = var1.responseText; if (data) { playSuccess ...

When I click on .toggle-menu, I want the data-target to have a toggled class and the other divs to expand

Looking to achieve a functionality where clicking on .toggle-menu will toggle a class on the specified data-target element and expand other divs accordingly. jQuery(document).ready(function() { var windowWidth = jQuery(window).width(); if (win ...

Display the content of a Vue file directly on the webpage

I am currently developing a website to showcase UI components using plain CSS. The project is built with Vue, utilizing standard HTML and CSS. One of the key features I am working on is providing users with two views for each component: 'Preview' ...

Tips for recognizing the click, such as determining which specific button was pressed

Currently, I am utilizing Angular 6. On the customer list page, there are three buttons - "NEW", "EDIT", and "VIEW" - all of which render to one component. As a result, it is crucial for me to determine which specific button has been clicked in order to ...

I encountered an issue in reactjs where I received the error message: TypeError: this.state.coords.map is not functioning properly

Here is the code snippet I wrote: import React, { Component } from 'react'; class LocationApp extends Component { constructor(props){ super(props) this.state = { coords:[], error:[], } } ...

Utilize the fetch function to showcase information retrieved from a specific endpoint on a webpage using Javascript

Hey there, I have a Node server with an http://localhost:3000/community endpoint. When I make a GET request to this endpoint, it returns information about three different users in the form of JSON objects. [ { "avatar": "http://localhost:3000/avatars ...

Importance of Security in ExpressJS' req and res Object ParametersSecurity is

In my express app, I wanted to ensure that the current user is logged in by implementing a global variable called current_user. To achieve this, I added a middleware: app.use(require('./controller/user').auth_user); In the user.js file, I creat ...

Why is it not possible for me to choose an element after it has been placed within a grid component?

Struggling to eliminate the blur effect on an image inside a grid element? It seems modifying the properties of an element within a grid class is causing some issues. To simplify, I conducted a basic test: I managed to change the ppp2 class when hovering ...

If I remove my project but still have it saved on my GitHub, do I need to reinstall all the dependencies or can I simply run npm install again?

I have a question regarding my deleted project that is saved on GitHub. If I formatted my PC and lost the project but it's still on GitHub, do I need to reinstall all the dependencies or can I just run 'npm install'? The project has dependen ...

Is there a method in bootstrap that reveals the elements with the hidden class?

Currently, I am loading data into a bootstrap table on my JSP. The table class is hidden at the moment. After successfully uploading the data into the bootstrap table, I have implemented the following code: $(function() { var table = $('#Table') ...

The issue arises when using multiple route files in Route.js, as it hinders the ability to incorporate additional functions within the

After breaking down Route.js into multiple controllers, I'm stuck on why I can't add an extra function to block permissions for viewing the page. // route.js module.exports = function(app, passport) { app.use('/profile&apos ...