Updating to AWS SDK V3 for SQS messaging in NodeJS Typescript
Published: 2023-03-03 05:59:20

As a Node.js developer, you might have come across scenarios where you need to work with different AWS services, such as Amazon SQS. In this article, we will explore how to load credentials from the environment variables, retrieve a message from SQS, update its visibility, and delete the message when the work is completed using TypeScript and the `client-sqs` library.

## Prerequisites

Before proceeding with this article, you need to have the following installed on your system:

*   Node.js and NPM installed.
*   An AWS account with access to Amazon SQS.
*   The AWS CLI configured with your credentials.

## Setting up the Environment


To begin, let's create a new Node.js project and install the required dependencies. Open your terminal and follow these steps:

1.  Create a new directory and navigate into it.
    
    ```
    mkdir sqs-example
    cd sqs-example
    ```
    
2.  Initialize a new Node.js project using NPM.
    
    ```
    npm init -y
    ```
    
3.  Install the `aws-sdk` and `client-sqs` packages as dependencies.
    
    ```
    npm install aws-sdk client-sqs
    ```
    

## Retrieving a Message from SQS

In this section, we will create a function to retrieve a message from SQS.

1.  Create a new file named `sqs.ts` in the project's root directory.
    
2.  Import the necessary modules and initialize a new SQS client.
    
    ```typescript
    import { fromEnv } from "@aws-sdk/credential-provider-env";
    import { SQSClient, ReceiveMessageCommand } from "@aws-sdk/client-sqs";
    
    const credentials = fromEnv();
    const sqsClient = new SQSClient({ credentials });
    ```
    
3.  Define a new function named `getMessage` that accepts a queue URL and returns a message.
    
    ```typescript
    async function getMessage(queueUrl: string): Promise<string> {
      const command = new ReceiveMessageCommand({
        QueueUrl: queueUrl,
        MaxNumberOfMessages: 1,
      });
    
      const response = await sqsClient.send(command);
    
      if (response.Messages?.length) {
        const message = response.Messages[0];
        return message.Body!;
      }
    
      throw new Error("No messages found in the queue");
    }
    ```
    
4.  Export the `getMessage` function.
    
    ```typescript
    export { getMessage };
    ```
    

## Updating Message Visibility

In this section, we will create a function to update the visibility of a message.

Add a new function named `updateMessageVisibility` to the `sqs.ts` file.
    
    ```typescript
    import { ChangeMessageVisibilityCommand } from "@aws-sdk/client-sqs";

    async function updateMessageVisibility(
      queueUrl: string,
      receiptHandle: string,
      visibilityTimeout: number
    ): Promise<void> {
      const command = new ChangeMessageVisibilityCommand({
        QueueUrl: queueUrl,
        ReceiptHandle: receiptHandle,
        VisibilityTimeout: visibilityTimeout,
      });
    
      await sqsClient.send(command);     
    }
    ```
  
    

## Deleting a Message

In this section, we will create a function to delete a message from SQS.

Add a new function named `deleteMessage` to the `sqs.ts` file.
    
```typescript  
import { DeleteMessageCommand } from "@aws-sdk/client-sqs";

    async function deleteMessage(queueUrl: string, receiptHandle
): Promise {  
  const command = new DeleteMessageCommand({
    QueueUrl: queueUrl,
    ReceiptHandle: receiptHandle,
  });

  await sqsClient.send(command);
}
```

## Putting it all Together

In this section, we will put together all the functions we created and create a new file to run our code.

1.  Create a new file named `index.ts` in the project's root directory.
    
2.  Import the `getMessage`, `updateMessageVisibility`, and `deleteMessage` functions from the `sqs.ts` file.
    
    ```typescript
    import { getMessage } from "./sqs";
    import { updateMessageVisibility } from "./sqs";
    import { deleteMessage } from "./sqs";
    ```
    
3.  Define the queue URL and visibility timeout.
    
    ```typescript
    const queueUrl = "https://sqs.<region>.amazonaws.com/<account-id>/<queue-name>";
    const visibilityTimeout = 60;
    ```
    
4.  Call the `getMessage` function to retrieve a message from the queue.
    
    ```typescript
    const message = await getMessage(queueUrl);
    ```
    
5.  Process the message and call the `updateMessageVisibility` function to extend its visibility timeout.
    
    ```typescript
    // TODO: DO THE WORK
    await updateMessageVisibility(queueUrl, receiptHandle, visibilityTimeout);
    ```
    
6.  Delete the message when the work is completed.
    
    ```typescript
    await deleteMessage(queueUrl, receiptHandle);
    ```
    

## Running the Code

To run the code, execute the following command in your terminal:

```terminal
ts-node index.ts
```

This will execute the `index.ts` file and retrieve a message from the queue. You can replace the TODO comment with your own code to process the message.

## Conclusion

In this article, we explored how to load credentials from the environment variables, retrieve a message from SQS, update its visibility, and delete the message when the work is completed using TypeScript and the `client-sqs` library. By following the steps outlined in this article, you should now be able to work with SQS in your Node.js applications with ease.

Remember to replace the placeholders in the `queueUrl` variable with your own values. Additionally, you can customize the `visibilityTimeout` variable to suit your needs.

We hope that this article has helped you learn how to work with SQS in your Node.js applications using TypeScript. Remember that this is just a starting point, and there is a lot more to learn about SQS and AWS services in general. If you want to learn more, we recommend checking out the official AWS documentation, as well as other resources available online. Happy coding!

### BONUS TESTS

We will be using the Jest testing framework to write our tests. If you haven't installed Jest yet, you can install it using the following command:

```
npm install --save-dev jest
```

After installing Jest, create a new file named `sqs.test.ts` in the same directory as the `sqs.ts` file. This file will contain our unit tests.

## Testing the getMessage Function

First, let's write a unit test for the `getMessage` function. The purpose of this function is to retrieve a message from SQS.

Here's an example test:

```typescript
import { getMessage } from "./sqs";
import { ReceiveMessageCommand } from "@aws-sdk/client-sqs";

jest.mock("@aws-sdk/client-sqs", () => ({
  SQSClient: jest.fn(() => ({
    send: jest.fn(),
  })),
  ReceiveMessageCommand: jest.fn(),
}));

describe("getMessage", () => {
  it("should retrieve a message from SQS", async () => {
    // Arrange
    const queueUrl = "https://sqs.<region>.amazonaws.com/<account-id>/<queue-name>";
    const messageBody = "test message";
    const receiptHandle = "test-receipt-handle";
    const command = new ReceiveMessageCommand({
      QueueUrl: queueUrl,
      MaxNumberOfMessages: 1,
      VisibilityTimeout: 0,
    });
    const expectedMessage = {
      Body: messageBody,
      ReceiptHandle: receiptHandle,
    };

    // Mock the SQS client to return the expected message
    const mockSend = jest.fn().mockResolvedValueOnce({
      Messages: [expectedMessage],
    });
    const SQSClient = require("@aws-sdk/client-sqs").SQSClient;
    SQSClient.prototype.send = mockSend;
    const ReceiveMessageCommand = require("@aws-sdk/client-sqs").ReceiveMessageCommand;
    ReceiveMessageCommand.mockReturnValueOnce(command);

    // Act
    const message = await getMessage(queueUrl);

    // Assert
    expect(mockSend).toHaveBeenCalledTimes(1);
    expect(ReceiveMessageCommand).toHaveBeenCalledTimes(1);
    expect(ReceiveMessageCommand).toHaveBeenCalledWith({
      QueueUrl: queueUrl,
      MaxNumberOfMessages: 1,
      VisibilityTimeout: 0,
    });
    expect(message).toEqual(expectedMessage);
  });
});
```

In this test, we are mocking the `client-sqs` library to return a test message. We then call the `getMessage` function and assert that it retrieves the expected message from SQS.

## Testing the updateMessageVisibility Function

Next, let's write a unit test for the `updateMessageVisibility` function. The purpose of this function is to update the visibility of a message in SQS.

Here's an example test:

```typescript
import { updateMessageVisibility } from "./sqs";
import { ChangeMessageVisibilityCommand } from "@aws-sdk/client-sqs";

jest.mock("@aws-sdk/client-sqs", () => ({
  SQSClient: jest.fn(() => ({
    send: jest.fn(),
  })),
  ChangeMessageVisibilityCommand: jest.fn(),
}));

describe("updateMessageVisibility", () => {
  it("should update the visibility of a message in SQS", async () => {
    // Arrange
    const queueUrl = "https://sqs.<region>.amazonaws.com/<account-id>/<queue-name>";
    const receiptHandle = "test-receipt-handle";
    const visibilityTimeout = 60;
    const command = new ChangeMessageVisibilityCommand({
      QueueUrl: queueUrl,
      ReceiptHandle: receiptHandle,
      VisibilityTimeout: visibilityTimeout,
    });

    // Mock the SQS client to return a successful response
    const mockSend = jest.fn().mockResolvedValueOnce({});
    const SQSClient = require("@aws-sdk/client-sqs").SQSClient;
    SQSClient.prototype.send = mockSend;
    const ChangeMessageVisibilityCommand = require("@aws-sdk/client-sqs").ChangeMessageVisibilityCommand;
    ChangeMessageVisibilityCommand.mockReturnValueOnce(command);

    // Act
    await updateMessageVisibility(queueUrl, receiptHandle, visibilityTimeout);

    // Assert
    expect(mockSend).toHaveBeenCalledTimes(1);
    expect(ChangeMessageVisibilityCommand).toHaveBeenCalledTimes(1);
    expect(ChangeMessageVisibilityCommand).toHaveBeenCalledWith({
      QueueUrl: queueUrl,
      ReceiptHandle: receiptHandle,
      VisibilityTimeout: visibilityTimeout,
    });
  });  
});

```

In this test, we are mocking the `client-sqs` library to return a successful response. We then call the `updateMessageVisibility` function and assert that it updates the message visibility in SQS.

## Testing the deleteMessage Function

Finally, let's write a unit test for the `deleteMessage` function. The purpose of this function is to delete a message from SQS.

Here's an example test:

```typescript
import { deleteMessage } from "./sqs";
import { DeleteMessageCommand } from "@aws-sdk/client-sqs";

jest.mock("@aws-sdk/client-sqs", () => ({
  SQSClient: jest.fn(() => ({
    send: jest.fn(),
  })),
  DeleteMessageCommand: jest.fn(),
}));

describe("deleteMessage", () => {
  it("should delete a message from SQS", async () => {
    // Arrange
    const queueUrl = "https://sqs.<region>.amazonaws.com/<account-id>/<queue-name>";
    const receiptHandle = "test-receipt-handle";
    const command = new DeleteMessageCommand({
      QueueUrl: queueUrl,
      ReceiptHandle: receiptHandle,
    });

    // Mock the SQS client to return a successful response
    const mockSend = jest.fn().mockResolvedValueOnce({});
    const SQSClient = require("@aws-sdk/client-sqs").SQSClient;
    SQSClient.prototype.send = mockSend;
    const DeleteMessageCommand = require("@aws-sdk/client-sqs").DeleteMessageCommand;
    DeleteMessageCommand.mockReturnValueOnce(command);

    // Act
    await deleteMessage(queueUrl, receiptHandle);

    // Assert
    expect(mockSend).toHaveBeenCalledTimes(1);
    expect(DeleteMessageCommand).toHaveBeenCalledTimes(1);
    expect(DeleteMessageCommand).toHaveBeenCalledWith({
      QueueUrl: queueUrl,
      ReceiptHandle: receiptHandle,
    });
  });
});
```

In this test, we are mocking the `client-sqs` library to return a successful response. We then call the `deleteMessage` function and assert that it deletes the message from SQS.

These unit tests will help you ensure that your code is working as expected and will help you catch bugs early on in the development process. You can run these tests using the following command:

```terminal
npm test
```

This will run all the tests in the `sqs.test.ts` file and output the results in your terminal.

We hope that this article has been helpful in guiding you on how to write unit tests for your Node.js TypeScript code using the Jest testing framework. Remember to write tests for all the functions in your code to ensure that it works as expected. Happy testing!