DEV Community

Tyler Steck
Tyler Steck

Posted on

Updating to AWS SDK V3 for SQS messaging in NodeJS Typescript

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.

    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.

    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.

    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);     
}
```
Enter fullscreen mode Exit fullscreen mode

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.

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);
}
Enter fullscreen mode Exit fullscreen mode

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.

    import { getMessage } from "./sqs";
    import { updateMessageVisibility } from "./sqs";
    import { deleteMessage } from "./sqs";
    
  3. Define the queue URL and visibility timeout.

    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.

    const message = await getMessage(queueUrl);
    
  5. Process the message and call the updateMessageVisibility function to extend its visibility timeout.

    // TODO: DO THE WORK
    await updateMessageVisibility(queueUrl, receiptHandle, visibilityTimeout);
    
  6. Delete the message when the work is completed.

    await deleteMessage(queueUrl, receiptHandle);
    

Running the Code

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

ts-node index.ts
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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:

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);
  });
});
Enter fullscreen mode Exit fullscreen mode

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:

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,
    });
  });  
});

Enter fullscreen mode Exit fullscreen mode

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:

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,
    });
  });
});
Enter fullscreen mode Exit fullscreen mode

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:

npm test
Enter fullscreen mode Exit fullscreen mode

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!

Top comments (0)