Simple Event-Driven App using Amazon MQ (RabbitMQ)
Tags: aws, rabbitmq, microservices, eventdriven
Published: 2022-08-14 13:38:00

Hello everyone! I'm back after busy work and want to introduce an event-driven app. I've explored and learned microservices and want to start from event-driven architecture. If you want to know more about Event-Driven Architecture, please visit this [page](https://aws.amazon.com/event-driven-architecture/).

## Preparation

I'm using Node.js for the sample app so you will need some tools like Node.js and Yarn. I'll give you another sample with different programming languages such as C# and Go in the next post. You may check this Github Repository to look around.

{% github bervProject/rabbitmq-demo %}

### Prepare the RabbitMQ in Amazon MQ

We will provision the RabbitMQ manually through the AWS Console.

1. Search Amazon MQ in the search box and click the Amazon MQ.

    ![Search Amazon MQ](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2zlpcne78pira1edyb2c.png)
 
2. Click `Get started` button.

    ![Get started](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f8jhzfr6ln9l6u6pikxj.png)
 
 
3. Select `RabbitMQ` and click `Next`.

     ![Select RabbitMQ](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vv0stebusn88xonfiwaw.png)
 

4. We will use `Single-instance broker` just for testing, you may use `Cluster deployment` for production use. And, click `Next`.

    ![Select Single-instance broker](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4oxt842g7djn2hlgbn9h.png)

5. Give the broker name and select `mq.t3.micro` instance.

    ![Give name](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2a1nboisv7znmylop7yu.png)

6. Setup the username and password for the RabbitMQ host.

    ![Setup username and password](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rne6h60i0f4hrlwtw9rg.png)

7. Open the `Additional setting`. Make sure you select the `Public access` and `Use the default VPC and subnet(s)`. Please avoid `Public access` for production use. You may need to use `Private access` and setup the VPC yourself for production use.

    ![Additional settings](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/agxbifguivymjpeaq69v.png)
 
8. Finally, click `Next`. Review your configuration, and click `Create broker`.

     ![Create Broker Review](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/abfo9cppdzywaspuz0gq.png)

## Time to Code!

* Init your project. Use `yarn init`. Input the details of your project.

    ![Init](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8m5ogghcblq8d8amyy3p.png)

* Install some dependencies.
  * `yarn add amqplib` - for send/consume message from/to RabbitMQ.
  * `yarn add uuid` - to generate message id and correlation id.

### Sender Code

```javascript
const amqp = require('amqplib');
const { v4: uuidv4 } = require('uuid');

// setup queue name
const queueName = 'test-queue';

/**
 * Send message
 */
async function send() {
  // connect to RabbitMQ
  const connection = await amqp.connect(process.env.RABBITMQ_HOST || 'amqp://localhost');
  // create a channel
  const channel = await connection.createChannel();
  // create/update a queue to make sure the queue is exist
  await channel.assertQueue(queueName, {
    durable: true,
  });
  // generate correlation id, basically correlation id used to know if the message is still related with another message
  const correlationId = uuidv4();
  // send 10 messages and generate message id for each messages
  for (let i = 1; i <= 10; i++) {
    const buff = Buffer.from(JSON.stringify({
      test: `Hello World ${i}!!`
    }), 'utf-8');
    const result = channel.sendToQueue(queueName, buff, {
      persistent: true,
      messageId: uuidv4(),
      correlationId: correlationId,
    });
    console.log(result);
  }
  // close the channel
  await channel.close();
  // close the connection
  await connection.close();
}

send();
```

### Consumer Code

```javascript
const amqp = require('amqplib');

// setup queue name
const queueName = 'test-queue';

/**
 * consume the message
 */
async function consume() {
  // setup connection to RabbitMQ
  const connection = await amqp.connect(process.env.RABBITMQ_HOST || 'amqp://localhost');
  // setup channel
  const channel = await connection.createChannel();
  // make sure the queue created
  await channel.assertQueue(queueName, {
    durable: true,
  });
  console.log(" [*] Waiting for messages in %s. To exit press CTRL+C", queueName);
  // setup consume
  channel.consume(queueName, function (message) {
    // just print the message in the console
    console.log("[%s] Received with id (%s) message: %s", message.properties.correlationId, message.properties.messageId, message.content.toString());
    // ack manually
    channel.ack(message);
  }, {
    // we use ack manually
    noAck: false,
  });
}

consume();
```

### Test Your Code

* Setup your environment variable, you may use the example scripts to setup the environment variable. As example:

    * For powershell
    
    ```powershell
     $env:RABBITMQ_HOST = 'amqps://<username>:<password>@<rabbitmq-endpoint>:<rabbitmqport>'
    ```

    * For Linux/MacOS
    
    ```sh
     export RABBITMQ_HOST=amqps://<username>:<password>@<rabbitmq-endpoint>:<rabbitmqport>
    ```

* Run the sender. Use `node sender.js`. You will get the console like this.

    ![Sender](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/t5sm2e0yq62c6t0k70lu.png)

* Run the consumer. Use `node consumer.js`. You will get the console like this.

    ![Consume](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6onku35qes92rs12cfv7.png)

## Congrats

Congrats! You've made a simple app to send and receive messages. Don't forget to clean up the resources if you do not use them again.

![GIF Congrats](https://media.giphy.com/media/BPJmthQ3YRwD6QqcVD/giphy.gif)