0

I'm using crypto.generateKeyPair inside a post endpoint in Express.

I need to insert the key generated inside my DB and then return within the endpoint the id row of the inserted row.

The endpoint code reads as:

app.post('/createKeys', (req, res) => {
crypto.generateKeyPair('rsa',,
   (err, publicKey, privateKey) => {
        if(!err) {
            let id = myfunction(publicKey.toString('hex'), 
                     privateKey.toString('hex'));

            console.log(id)
        } else {
            res.status(500).send(err);
        }
    });
});

async function myfunction(publicKey, privateKey) {
  await pool.query('INSERT INTO users (publickey, privatekey) VALUES ($1, $2) RETURNING id', 
    [publicKey, privateKey], 
    (error, results) => {
        if (error) {
          throw error;
        }
        resolve(results.rows[0]['id']);
  });
};

However, inside the callback in crypto I get only a Promise, or undefined if I don't use async/await. How can I await myfunction result so I can send back to the user the id?

Learning from masters
  • 2,032
  • 3
  • 29
  • 42

1 Answers1

0

Several issues here:

await only does something useful when you are awaiting a promise.

pool.query() does not return a promise when you pass it a callback, so your await there is not doing anything useful.

resolve() is not a function that exists outside the context of creating a new promise with new Promise((resolve, reject) => { code here })

throw error inside an asynchronous callback will not do anything useful as there is no way to catch that exception and thus no way to implement any decent error handling. Don't write code that way. When you promisify the function (as shown below), you can then reject the promise and that will offer a way to propagate the error back to the caller.

Your choices for waiting for pool.query() with await here are:

  1. Use the version of your database that natively supports promises and then don't pass a callback to pool.query() so that it returns a promise that tells you when it's complete.

  2. Promisify your own function by wrapping pool.query() in a new promise and call resolve() and reject() appropriately.

Remember, do NOT mix plain callbacks and promise. Instead, promisify any asynchronous functions that use plain callbacks and then do all your logic flow with promises.

Here's a manually promisified version of your myfunction():

function myfunction(publicKey, privateKey) {
    return new Promise((resolve, reject) => {
        pool.query('INSERT INTO users (publickey, privatekey) VALUES ($1, $2) RETURNING id',
            [publicKey, privateKey],
            (error, results) => {
                if (error) {
                    reject(error);
                    return;
                }
                resolve(results.rows[0]['id']);
            });
    });
}

crypto.generateKeyPairP = util.promisify(crypto.generateKeyPair);


app.post('/createKeys', async (req, res) => {
    try {
        const {publicKey, privateKey } = await crypto.generateKeyPairP('rsa');
        const id = await myfunction(publicKey.toString('hex'), privateKey.toString('hex'));
        console.log(id);
        // send your response here, whatever you want it to be
        res.send(id);

    } catch(e) {
        res.status(500).send(e);
    }    
});

Note that in this implementation, the resolve and reject functions come from the new Promise() - they don't just exist in this air.

But, there is likely a version of your database or an interface in your existing database module where pool.query() can return a promise directly using built-in promise support.

jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • ok, so I promised as you showed me, and then I also passed res from the post to myfunction so I could answer the user. It works! Appart from this, it might be useful to use a sync generatekeypair https://stackoverflow.com/a/61491656/1200914 – Learning from masters Feb 02 '23 at 00:00
  • 1
    @Learningfrommasters - Note a recent edit that adds a cleaner implementation of calling `crypto.generateKeyPair()` using promises for it too. – jfriend00 Feb 02 '23 at 00:05