Handling aws-sdk bucket creation requests nodejs express

Viewed 252

I have the following code. The main aim is to create a bucket through aws-sdk and in case of success, generate a database record for the relevant entry. But there are a few issues

const AWS  = require('aws-sdk');
const router = require('express').Router();

const Bucket = require("../../../models/bucket_bucket");

const { v4: uuidv4 } = require('uuid');

require('dotenv').config();

AWS.config.logger = console;

s3 = new AWS.S3({
    // apiVersion: '2006-03-01',
    accessKeyId: process.env.AWS_ACCESS_KEY_ID,
    secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
    region: process.env.AWS_REGION
});

// Accepts Bucket Name, Bucket Created User/ Belonging Organization
router.post('/create', (req, res, next)=>{

    var requestBucketParams = {
        Bucket: req.body.bucket
    }

    var isBucketCreationSuccess = false;

    var createRequestBucketPromise = s3.createBucket(requestBucketParams).promise();
    var createDeliveryBucketPromise = s3.createBucket(deliveryBucketParams).promise();

    createRequestBucketPromise.then((data)=>{
        isBucketCreationSuccess = true;
    }).catch((err)=>{
        res.send({
            message: "error while creating the bucket",
            description: err.message
        });
    });
    
    console.log(isBucketCreationSuccess);

        
    if(isBucketCreationSuccess){
        Bucket.create({
            bucketId: uuidv4(),
            name: req.body.bucket,
            bucket_url: "http://" + req.body.bucket + ".s3.amazonaws.com",
        }).then(bucket=>{
            res.send({
                url: "http://" + req.body.bucket + ".s3.amazonaws.com",
            })
        }).catch(err=>{
            res.send({
                message: err.message
            })
        })
    }else{
        res.send({
            message: "Database record creation failed"
        })
    }

});

So there are 2 questions that I have (Please bear with me I am not much familiar with nodejs)

  1. isBucketCreationSuccess is always false no matter how I try to set it. I do believe this is something to do with async. But it doesn't work
  2. This generates the following errors
(node:25293) UnhandledPromiseRejectionWarning: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
   at ServerResponse.setHeader (_http_outgoing.js:543:11)
   at ServerResponse.header (/home/caesar/Workspace/res-s3/res-s3-backend/node_modules/express/lib/response.js:771:10)
   at ServerResponse.send (/home/caesar/Workspace/res-s3/res-s3-backend/node_modules/express/lib/response.js:170:12)
   at ServerResponse.json (/home/caesar/Workspace/res-s3/res-s3-backend/node_modules/express/lib/response.js:267:15)
   at ServerResponse.send (/home/caesar/Workspace/res-s3/res-s3-backend/node_modules/express/lib/response.js:158:21)
   at /home/caesar/Workspace/res-s3/res-s3-backend/routes/api/bucket/index.js:54:13
   at processTicksAndRejections (internal/process/task_queues.js:93:5)
(Use `node --trace-warnings ...` to show where the warning was created)
(node:25293) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 1)
(node:25293) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
[AWS s3 409 2.197s 0 retries] createBucket({
 Bucket: 'delivery-bucket-1',
 CreateBucketConfiguration: { LocationConstraint: 'eu-west-1' }
})
1 Answers

The problem you are facing has nothing to do with Nodejs. I suggest refreshing what you know about Promise and Async/Await

The code that you have shared seems incomplete. I will try to explain the idea with what you have shared.

Code statements outside of the then/catch are executed synchronously.

createRequestBucketPromise.then((data)=>{
  isBucketCreationSuccess = true;
})
.catch((err)=>{
  res.send({
    message: "error while creating the bucket",
    description: err.message
  });
});

// This will get executed before above promise handler
console.log(isBucketCreationSuccess);

The promise shown below doesn't have an error handler causing the warning

var createDeliveryBucketPromise = s3.createBucket(deliveryBucketParams).promise();

You could do something like this to solve the problem but I recommend the second option i.e. using Async

Using Promise

router.post('/create', (req, res, next)=>{
    // const or let is prefered over var
    const requestBucketParams = {
        Bucket: req.body.bucket
    },
    bucketUrl = `https://${req.body.bucket}.s3.amazonaws.com`;

    return s3.createBucket(requestBucketParams).promise()
    .then((data) => {
        return Bucket.create({
            bucketId: uuidv4(),
            name: req.body.bucket,
            bucket_url: bucketUrl,
        });
    })
    .then((bucket) => {
        return res.send({
            url: bucketUrl,
        });
    })
    .catch((err)=>{
        // try to differentiate between aws error and you database error
        // and respond. There are multiple ways to achieve that
    });
});

Using Async/Await

router.post('/create', async (req, res, next) => {
    // const or let is prefered over var
    const requestBucketParams = {
        Bucket: req.body.bucket
    },
    bucketUrl = `https://${req.body.bucket}.s3.amazonaws.com`;

    try {
        await s3.createBucket(requestBucketParams).promise();
    } catch (err) {
        return res.send({
            message: "error while creating the bucket",
            description: err.message
        });
    }
    
    try{
        await Bucket.create({
            bucketId: uuidv4(),
            name: req.body.bucket,
            bucket_url: bucketUrl,
        });
    } catch (err) {
        return res.send({
            message: "Database record creation failed"
        });
    }

    return res.send({
        url: bucketUrl,
    });
});
Related