Frequently Asked Questions
Set up an endpoint in your Fastify app, configure it as the callback URL in your SNS topic settings, and implement logic to handle subscription confirmations and delivery status notifications at that endpoint. This allows your Fastify application to receive real-time updates on message delivery status directly from SNS. Don't forget to verify the message signature for security.
The `sns-validator` library is used to verify the authenticity of incoming messages from AWS SNS to your Fastify application. This is a crucial security measure to ensure that the notifications you're receiving are actually from AWS and haven't been tampered with.
The `AWS.SNS.SMS.SMSType` attribute is required when publishing SMS messages via SNS if you want to receive delivery status notifications. Set it to 'Transactional' for critical messages and 'Promotional' for other types of SMS messages. This lets SNS know to generate delivery reports.
Use 'Transactional' for critical messages requiring high reliability, such as one-time passwords (OTPs) or purchase confirmations. Use 'Promotional' for marketing or informational messages where cost optimization is preferred. This setting affects message delivery priority and pricing.
Yes, you can confirm SNS subscriptions programmatically in your Fastify application using the `SubscribeURL` provided in the subscription confirmation message. Fetching this URL confirms the subscription. However, for production, consider adding security checks (e.g., TopicArn verification) or manual subscription management through the AWS console.
Enable delivery status logging by setting the 'AWS.SNS.SMS.SMSType' message attribute to 'Transactional' or 'Promotional' when publishing messages. This applies when publishing directly to phone numbers or if you are setting per-message configurations instead of configuring it on the topic itself.
`MessageAttributes` in the AWS SNS `PublishCommand` allow you to include metadata with your messages, such as the 'AWS.SNS.SMS.SMSType' to enable delivery reports or custom identifiers for your application. These attributes are key for configuring delivery status logging and other message-specific options.
Use a `switch` statement or similar logic to differentiate between 'SubscriptionConfirmation', 'Notification', and 'UnsubscribeConfirmation' message types received at your Fastify callback endpoint. The 'Type' field in the SNS message JSON indicates the message type.
A 403 Forbidden error from your Fastify SNS callback endpoint usually means that signature verification failed. This indicates a potential security issue, and you should not process the message. Check your signature validation logic and ensure it is correctly implemented using the sns-validator.
Secure the endpoint using signature verification with the `sns-validator` library. Validate the message signature against the certificate provided in the SNS message header. This prevents processing of forged messages and ensures the integrity of your notifications.
You need access to the raw request body (`rawBody: true`) in your Fastify route configuration to perform signature verification with the `sns-validator` library. The raw body contains the original, unmodified message content required for validation.
You'll need a Node.js environment with npm/yarn, an AWS account with SNS and IAM permissions, AWS credentials configured, a publicly accessible URL for your Fastify app, and basic familiarity with Fastify, JavaScript/TypeScript, and AWS SDK setup.
Use the AWS SDK for JavaScript v3 (@aws-sdk/client-sns) with the `PublishCommand`, providing the `TopicArn` instead of a direct phone number. Ensure the topic has appropriate delivery logging settings. Optionally, define per-message attributes.
In the 'Notification' type message handling section of your Fastify callback endpoint, parse the nested JSON string in the 'Message' field to access the actual delivery status (e.g., 'DELIVERED', 'FAILED') and other information provided by SNS. Then, implement your application logic (e.g., database updates) based on this status.
This guide provides a step-by-step walkthrough for building a Fastify application that not only publishes messages using AWS Simple Notification Service (SNS) but also reliably receives and processes delivery status notifications from SNS via HTTP/S callbacks.
We'll cover everything from initial project setup and AWS configuration to building the callback endpoint, handling SNS message types (including subscription confirmation and notifications), ensuring security through signature verification, and best practices for logging, error handling, and deployment.
Project Goal:
To create a robust Fastify application capable of:
DELIVERED
,FAILED
) for those messages via an HTTP/S endpoint exposed by the Fastify application.Problem Solved:
By default, when you publish a message to SNS, you get confirmation that SNS accepted the message, but not necessarily that the message reached the end recipient (like a specific phone number or mobile app). Implementing delivery status callbacks provides crucial visibility into the final delivery outcome, enabling applications to track message success rates, retry failed messages intelligently, or update internal records based on delivery status.
Technologies Used:
@aws-sdk/client-sns
: Specific AWS SDK v3 package for SNS.sns-validator
: A utility library to validate the authenticity of incoming messages from AWS SNS, crucial for security.dotenv
: Module to load environment variables from a.env
file intoprocess.env
.System Architecture:
Prerequisites:
ngrok
can be used during development.1. Setting up the Project
Let's initialize our Node.js project and install the necessary dependencies.
1.1. Create Project Directory & Initialize
Open your terminal and run the following commands:
1.2. Install Dependencies
1.3. Project Structure
Create the following basic file structure:
1.4. Configure
.gitignore
Create a
.gitignore
file and addnode_modules
and.env
to prevent committing sensitive information and dependencies:1.5. Environment Variables (
.env
)Create a
.env
file to store sensitive configuration and credentials.Explanation:
.env
keeps them separate from the codebase, enhancing security and portability.dotenv
loads these variables intoprocess.env
when the application starts.YOUR_...
) must be replaced with your real configuration details obtained from AWS and your deployment environment.1.6. Configuration File (
config.js
)This file centralizes access to environment variables with potential defaults.
Explanation:
dotenv
first.2. Implementing Core Functionality: Sending & Receiving
Now, let's build the core Fastify server and implement the logic for sending messages and handling the incoming SNS callbacks.
2.1. Basic Fastify Server (
server.js
)Explanation:
config.js
.pino-pretty
is conditionally used for better readability during development. Production uses standard JSON logging.SnsClient
, providing the region and credentials from our configuration.sns-validator
. Since itsvalidate
method uses callbacks, wepromisify
it for easier use withasync/await
.start
function launches the server, listening on the configured port and0.0.0.0
(important for containerized environments or VMs). Crucially, it logs the expected callback endpoint URL for verification.2.2. Sending Messages with Delivery Status
To receive delivery status notifications, you must configure SNS to send them when publishing the message. This is done via
MessageAttributes
in thePublishCommand
.Add the following route to
server.js
(before thestart()
call):Explanation:
/send-sms
POST route.phoneNumber
andmessageBody
. A simple E.164 format check is included. (Schema validation is added in Section 3).PublishCommand
parameters, we setMessageAttributes
:AWS.SNS.SMS.SMSType
: Setting this toTransactional
(orPromotional
) tells SNS you want delivery status logging enabled for SMS. Transactional is optimized for reliability, Promotional for cost. This attribute is required for SMS delivery status.AWS.SNS.SMS.SenderID
: An optional custom ID displayed on the recipient's device (subject to carrier support and registration requirements).snsClient.send(command)
to publish the message.MessageId
returned by SNS, which is essential for correlating status updates later.Alternative Approach (Publishing to Topic):
If you prefer publishing to a Topic ARN (
config.aws.snsTopicArn
) instead of directly to a phone number, the structure is similar, but you useTopicArn
instead ofPhoneNumber
. Ensure the Topic itself has delivery status logging configured (see Section 4.3) OR that theMessageAttributes
needed for the specific subscriber type (e.g., SMS, Application) are included in the publish command.2.3. Receiving SNS Callbacks
This is the endpoint SNS will call with subscription confirmations and delivery status notifications.
Add the following route to
server.js
(before thestart()
call):Explanation:
/sns-callbacks
POST route. ConfiguresrawBody: true
to access the raw request body needed for signature verification. Includes a check to ensurerawBody
is available.rawBody
string into a JavaScript object (message
). Handles potential JSON parsing errors.validateSnsMessage(message)
to check the signature. This is critical for security. Logs success or failure. Returns403 Forbidden
immediately on validation failure.switch
statement): Uses theType
field from the parsed JSON body (message.Type
) to determine the action:SubscriptionConfirmation
: Logs receipt, validatesSubscribeURL
, then makes an HTTP GET request to it usingnode-fetch
(dynamically imported for ESM compatibility). Logs success or failure of the confirmation attempt. Returns200 OK
to SNS to acknowledge the receipt of the confirmation message, regardless of whether the subsequent GET succeeded, to prevent SNS retries of the confirmation message itself.Notification
: Logs receipt. Parses the nested JSON string withinmessage.Message
to get the actual status details (notificationData
). Logs key fields likestatus
and the originalmessageId
. This is the primary integration point for your application logic (e.g., database updates). Returns200 OK
to SNS.UnsubscribeConfirmation
: Logs the event. Returns200 OK
.default
: Handles unexpected types. Returns400 Bad Request
.200 OK
promptly after successfully receiving and acknowledging a valid message (Notification, SubscriptionConfirmation, UnsubscribeConfirmation). Internal processing errors (like database failures) should be logged and handled asynchronously (e.g., via a queue) rather than causing the endpoint to return 5xx to SNS, which would trigger SNS retries. Signature validation failures return403
. Parsing errors or unknown types return400
.3. Building a Complete API Layer (Example)
Let's refine the
/send-sms
endpoint with validation and authentication.Explanation of Refinements:
preHandler
): We add apreHandler
hook that calls our exampleauthenticate
function. This hook is a basic placeholder. Replace it with a robust authentication mechanism (@fastify/jwt
,@fastify/auth
, OAuth, etc.) and use secure key comparison. Load secrets from environment variables.schema
for thebody
andresponse
. Fastify automatically validates incoming request bodies against thebody
schema. If validation fails, it sends a 400 error response automatically. This replaces the manual validation checks. Theresponse
schema helps document and potentially validate outgoing responses.^\\+[1-9]\\d{1,14}$
is correctly defined as a string within the JSON schema.catch
block now focuses on errors during thesnsClient.send
call, as input validation is handled by Fastify.Testing with cURL: