JavaScript

LocalStack SQS Node.js Example

Hello. In this tutorial, we will interact with Amazon AWS SQS (simple queue service) to create a Node.js app with the help of a popular emulator known as LocalStack.

1. Introduction

Localstack is an aws cloud service emulator that runs in a single container and allows us to run the aws applications on our local machines without connecting to a remote cloud provider. This tool helps to speed up and simplify the testing and development workflow. To work with localstack we will use docker so that we have the setup ready within minutes and without any dependency. If someone needs to go through the Docker installation, please watch this video.

1.1 Setting up Node.js

To set up Node.js on windows you will need to download the installer from this link. Click on the installer (also include the NPM package manager) for your platform and run the installer to start with the Node.js setup wizard. Follow the wizard steps and click on Finish when it is done. If everything goes well you can navigate to the command prompt to verify if the installation was successful as shown in Fig. 1.

LocalStack SQS Node.js - npm installation
Fig. 1: Verifying node and npm installation

1.2 Setting up LocalStack

To set up the localstack on our machine we will write a simple docker-compose file and execute the docker-compose commands to quickly start/stop the container. Add the below code the yml file to have the container up and running in minutes –

docker-compose.yml

01
02
03
04
05
06
07
08
09
10
11
12
13
14
services:
  localstack:
    environment:
      - AWS_DEFAULT_REGION=ap-southeast-1
      - EDGE_PORT=4566
      - SERVICES=sqs
    image: "localstack/localstack:latest"
    ports:
      - "4566:4566"
    container_name: docker-localstack
    volumes:
      - "${TMPDIR:-/tmp/localstack}:/tmp/localstack"
      - "/var/run/docker.sock:/var/run/docker.sock"
version: "3.0"

You’re free to change the details in the above yml file as per your requirements. For this tutorial, I am keeping some of the details are default.

1.3 Start/Stop the container

To start the localstack container we will execute the docker-compose command in the terminal window. Open a new terminal, navigate to the path containing the yml file and execute the below command.

Start the container

1
2
-- pull the image, create and start the container --
docker compose up -d

The above command will start the container on port – 4566 in daemon mode. If you will be doing this activity for the first time then the Docker tool will first pull the image from the hub repository. You can hit the health endpoint in the browser to confirm that the localstack container is up and running successfully.

Localstack health check endpoint

1
2
-- to check whether the localstack is up and running --

Once you are done with the tutorial use the below command to stop and remove the running localstack container.

Stop and remove the container

1
2
-- stop and remove the container –
docker compose down

2. LocalStack SQS Node.js Example

To set up the application, we will need to navigate to a path where our project will reside. For programming stuff, I am using Visual Studio Code as my preferred IDE. You’re free to choose the IDE of your choice.

2.1 Setting up the implementation

Let us write the different files which will be required for practical learning.

2.1.1 Setting up dependencies

Navigate to the project directory and run npm init -y to create a package.json file. This file holds the metadata relevant to the project and is used for managing the project dependencies, script, version, etc. Add the following code to the file wherein we will specify the required dependencies.

package.json

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
{
  "name": "localstack-sqs",
  "version": "1.0.0",
  "description": "localstack sqs",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "dev": "nodemon index.js"
  },
  "keywords": [
    "localstack sqs",
    "nodejs",
    "expressjs"
  ],
  "author": "javacodegeeks",
  "license": "ISC",
  "dependencies": {
    "aws-sdk": "^2.1043.0",
    "express": "^4.17.1",
    "underscore": "^1.13.1"
  },
  "devDependencies": {
    "nodemon": "^2.0.15"
  }
}

To download the dependencies navigate to the directory path containing the file and use the npm install command. If everything goes well the dependencies will be loaded inside the node_modules folder and you are good to go with the further steps.

2.1.2 Creating an environment config

Once the localstack is up and running we will be creating the environment configuration file required for this tutorial. Add the below code to the file present in the config folder. The file contains the localstack service endpoint and other required information for this tutorial.

env.js

01
02
03
04
05
06
07
08
09
10
11
12
const env = {
  REGION: "ap-southeast-1", // should match the one given in the docker-compose.yml file
  ACCOUNT_ID: "000000000000", // represents the dummy aws account id for localstack
  IAM: {
    ACCESS_KEY_ID: "na",
    SECRET_ACCESS_KEY: "na"
  },
  SERVICE_ENDPOINT: "http://localhost:4566", // represents the service point for localstack
  QUEUE_NAME: "geek" // queue name used in this tutorial for implementation
};
 
module.exports = env;

2.1.3 Creating a request handler class

Create a request handler class that will be responsible to handle the incoming requests from the client. The file will also contain the information about the sqs service object creation. Add the below code to the file present in the handler folder.

requests.js

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
const _ = require("underscore");
 
const env = require("../config/env");
 
// configuring local-stack aws
const AWS = require("aws-sdk");
AWS.config.update({
  region: env.REGION
});
 
// create an sqs service object
const sqs = new AWS.SQS({
  endpoint: env.SERVICE_ENDPOINT,
  accessKeyId: env.IAM.ACCESS_KEY_ID,
  secretAccessKey: env.IAM.SECRET_ACCESS_KEY,
  region: env.REGION
});
 
// handler methods
 
// method1 - to get sqs config status
const getConfigurationStatus = (req, res) => {
  console.log("fetching configuration status");
  res.status(200).json({
    status: "ok",
    data: sqs
  });
};
 
// method2 - to list all queues
const listQueues = (req, res) => {
  console.log("fetching all queues");
  sqs.listQueues({}, (err, data) => {
    if (err) {
      res.status(500).json({
        status: "internal server error",
        error: err
      });
    } else {
      res.status(200).json({
        status: "ok",
        urls: data.QueueUrls
      });
    }
  });
};
 
// method3 - to create a new sqs queue
const createQueue = (req, res) => {
  let createParams = {
    QueueName: env.QUEUE_NAME, // in real example client should provide queue name
    Attributes: {
      DelaySeconds: "60",
      MessageRetentionPeriod: "86400"
    }
  };
  console.log(createParams);
 
  sqs.createQueue(createParams, (err, data) => {
    if (err) {
      res.status(500).json({
        status: "internal server error",
        error: err
      });
    } else {
      res.status(201).json({
        status: "created",
        name: data.QueueName,
        message: "queue created successfully"
      });
    }
  });
};
 
// method4 - to purge the given sqs queue
const purgeQueue = (req, res) => {
  let queueName = req.query.name;
  if (_.isEmpty(queueName)) {
    res.status(400).json({
      status: "bad request",
      message: "queue name cannot be empty"
    });
  } else {
    console.log("purging queue = " + queueName);
 
    let purgeParams = {
      QueueUrl: env.SERVICE_ENDPOINT + "/" + env.ACCOUNT_ID + "/" + queueName
    };
    console.log(purgeParams);
 
    sqs.purgeQueue(purgeParams, (err, data) => {
      if (err) {
        res.status(500).json({
          status: "500",
          err: err
        });
      } else {
        res.status(200).json({
          status: "ok",
          data: "queue purged"
        });
      }
    });
  }
};
 
// method5 - to publish the message to the sqs queue
const publishMsg = (req, res) => {
  //todo - add empty queue name validation in the request body
 
  let msg = {
    id: req.body["id"],
    name: req.body["name"],
    email: req.body["email"],
    phone: req.body["phone"]
  };
 
  let msgParams = {
    QueueUrl:
      env.SERVICE_ENDPOINT + "/" + env.ACCOUNT_ID + "/" + req.body["queueName"], // queueName will never be empty
    MessageBody: JSON.stringify(msg)
  };
  console.log(msgParams);
 
  sqs.sendMessage(msgParams, (err, data) => {
    if (err) {
      res.status(500).json({
        status: "internal server error",
        error: err
      });
    } else {
      res.status(202).json({
        status: "accepted",
        messageId: data.MessageId,
        message: "sent to queue"
      });
    }
  });
};
 
// method6 - to receive the message from the sqs queue
const receiveMsg = (req, res) => {
  let queueName = req.query.name;
  if (_.isEmpty(queueName)) {
    res.status(400).json({
      status: "bad request",
      message: "queue name cannot be empty"
    });
  } else {
    console.log("Fetching messages from queue = " + queueName);
 
    let receiveParams = {
      QueueUrl: env.SERVICE_ENDPOINT + "/" + env.ACCOUNT_ID + "/" + queueName,
      MessageAttributeNames: ["All"]
    };
    console.log(receiveParams);
 
    sqs.receiveMessage(receiveParams, (err, data) => {
      if (err) {
        res.status(500).json({
          status: "internal server error",
          error: err
        });
      } else {
        if (!data.Messages) {
          res.status(200).json({
            status: "ok",
            data: "no message in the queue"
          });
        } else {
          let items = [];
          _.each(data.Messages, function (message) {
            let ele = {
              id: message.MessageId,
              receiptHandle: message.ReceiptHandle,
              data: JSON.parse(message.Body)
            };
            items.push(ele);
          });
          res.status(200).json({
            status: "ok",
            messages: items
          });
        }
      }
    });
  }
};
 
// method7 - to delete the message from the sqs queue
const deleteMsg = (req, res) => {
  let id = req.query.id;
  let queueName = req.query.name;
  if (_.isEmpty(id) || _.isEmpty(queueName)) {
    res.status(400).json({
      status: "bad request",
      message: "receipt handle id or queue name cannot be empty"
    });
  } else {
    console.log("Deleting message id = " + id + " from queue");
 
    let deleteParams = {
      QueueUrl: env.SERVICE_ENDPOINT + "/" + env.ACCOUNT_ID + "/" + queueName,
      ReceiptHandle: id
    };
    console.log(deleteParams);
 
    sqs.deleteMessage(deleteParams, (err, data) => {
      if (err) {
        res.status(500).json({
          status: "internal server error",
          error: err
        });
      } else {
        res.status(202).json({
          status: "accepted",
          message: "message deleted from queue"
        });
      }
    });
  }
};
 
module.exports = {
  getConfigurationStatus,
  listQueues,
  createQueue,
  purgeQueue,
  publishMsg,
  receiveMsg,
  deleteMsg
};

2.1.4 Creating a routing class

Create a routing class that will be responsible to map the incoming requests from the client with the handler method. Add the below code to the file present in the routes folder.

routes.js

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
const express = require("express");
const router = express.Router();
 
const {
  getConfigurationStatus,
  listQueues,
  createQueue,
  publishMsg,
  receiveMsg,
  deleteMsg,
  purgeQueue
} = require("../handler/requests");
 
// get sqs config status
router.get("/status", getConfigurationStatus);
 
// list sqs queues
router.get("/list", listQueues);
 
// create a sqs queue
router.post("/create", createQueue);
 
// purge a sqs queue
router.delete("/purge", purgeQueue);
 
// send message to sqs queue
router.post("/send", publishMsg);
 
// receive message from sqs queue
router.get("/receive", receiveMsg);
 
// delete message from sqs queue
router.delete("/delete", deleteMsg);
 
module.exports = {
  appRoutes: router
};

2.1.5 Creating an index file

Create an index file that will be acting as the entry point for the application.

index.js

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
// application
const express = require("express");
 
const app = express();
app.use(express.json());
 
// application routes
const routes = require("./routes/routes");
app.use("/", routes.appRoutes);
 
// start application
const port = process.env.PORT || 6001;
app.listen(port, () => {
  console.log(`Service endpoint = http://localhost:${port}`);
});

3. Run the Application

To run the application navigate to the project directory and enter the following command as shown below.

Command

1
$ nodemon

If everything goes well the application will be started successfully on port – 6001 as shown in Fig. 2.

LocalStack SQS Node.js - app run
Fig. 2: Application run

4. Demo

You are free to use postman or any other tool of your choice to make the HTTP request to the application endpoints. Download the endpoints collection from the downloads section for an easy setup.

Application endpoints

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
// get sqs config status
// http get endpoint - http://localhost:6001/status
 
// list sqs queues
// http get endpoint - http://localhost:6001/list
 
// create a sqs queue
// http post endpoint - http://localhost:6001/create
 
// purge a sqs queue
// http delete endpoint - http://localhost:6001/purge
 
// send message to sqs queue
// http post endpoint - http://localhost:6001/send
 
// receive message from sqs queue
// http get endpoint - http://localhost:6001/receive
 
// delete message from sqs queue
// http delete endpoint - http://localhost:6001/delete

That is all for this tutorial and I hope the article served you with whatever you were looking for. Happy Learning and do not forget to share!

5. Summary

Localstack is the most popular aws cloud emulator which is used by individuals to run the aws applications on local machines and without connecting to the remote cloud provider. In this tutorial, we learned how to set up the localstack and integrate it with a simple nodejs application. You can download the source code and the postman collection from the Downloads section.

6. Download the Project

This was a tutorial to understand the aws cloud emulator popularly known as the localstack and its simple working in a nodejs application.

Download
You can download the full source code of this example here: LocalStack SQS Node.js Example

Yatin Batra

An experience full-stack engineer well versed with Core Java, Spring/Springboot, MVC, Security, AOP, Frontend (Angular & React), and cloud technologies (such as AWS, GCP, Jenkins, Docker, K8).
Subscribe
Notify of
guest


This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Back to top button