Developer Guide: Implementing Two-Way SMS with Fastify and MessageBird - code-examples -

Frequently Asked Questions

Start by setting up a new Node.js project and installing Fastify, the MessageBird SDK, and necessary dependencies like dotenv, qs, async-retry, and @fastify/rate-limit. Create a project structure with directories for source code, routes, and environment variables. Finally, set up your .env file with your MessageBird API key, webhook signing key, and originator information from your MessageBird dashboard. Use `npm run dev` to start the server in development mode with automatic restarts, or `npm start` for production.
It's crucial for verifying the authenticity of incoming webhooks, ensuring they originate from MessageBird. It's used to generate a signature that your application compares against the signature received in the webhook request headers, preventing unauthorized access to your application from external sources and potential malicious actors.
Because MessageBird webhooks use the 'application/x-www-form-urlencoded' content type. It's necessary to handle webhook signature verification which requires the raw request body. The custom parser added in `app.js` stores the raw body string and then parses the form data reliably using the `qs` library. This raw body is needed in src/routes/webhook.js within the verifyMessageBirdSignature function.
Once your server is running, send a POST request to the /api/send-message endpoint with a JSON payload containing the recipient's phone number and message content. Use a tool like curl or Postman to send the request, ensuring a valid E.164 formatted number is used. You should receive a JSON response with the message ID and status. Make sure that validation is done at the API endpoint. Failure to comply with the format will result in error messages being returned.
In the MessageBird dashboard, go to Flow Builder, create a custom flow triggered by SMS, and choose your number. In the next step, select "Forward to URL" and paste your ngrok HTTPS URL with the secret path from your .env file. Use POST and no authentication, then save and publish. Ensure that ngrok is used to tunnel the SMS message between your Fastify app and MessageBird. Note that changes to the webhook path should also be reflected in the Flow Builder configuration.
ngrok creates a secure tunnel from a public URL to your localhost server, essential for development as it makes your local app accessible to MessageBird's webhooks. Run 'ngrok http 3000' to create the tunnel and use the HTTPS URL it provides when configuring the webhook in MessageBird's flow builder.
The provided code implements a pre-handler hook that verifies the signature using an HMAC-SHA256 algorithm with your webhook signing key. The hook extracts necessary data from the header and combines the raw body of the request with the message timestamp to recompute the signature. If verification fails, the hook returns a 401 error, thereby blocking all other handlers.
Use async-retry for outgoing API calls (like sending SMS replies) to gracefully handle temporary network issues or MessageBird API hiccups. The code example demonstrates its use with messagebird.messages.create, retrying up to 3 times with exponential backoff. The provided code demonstrates the use of async-retry and how to use the library.
It signals to MessageBird that you've received and processed the webhook successfully, preventing MessageBird from retrying the webhook. This should be done *even if your app fails to send the reply*. Reply failures should be handled asynchronously using techniques such as queues. This helps make the application robust against transient failures without being retried unnecessarily by MessageBird.
Use a long, random secret path for your webhook URL, stored in the WEBHOOK_SECRET_PATH environment variable. The code checks for this variable and prevents start-up if the path is too short or missing. This increases security making it more difficult to discover and trigger your endpoint from external sources.
The app.js file demonstrates a best practice for handling errors. The provided code includes a global error handler, which helps to consistently format all error responses from the application and enhances maintainability. The response formatting also distinguishes between client errors and server errors. This helps the user differentiate between errors which were caused by them and those where the server was responsible.
pino-pretty formats Fastify's log output for better readability during development. In production, Fastify uses JSON logging which can be processed by logging and monitoring systems. Log levels are set via the LOG_LEVEL environment variable. This helps enhance debugging locally during development while optimizing for log aggregators.
Create separate directories for routes (webhook.js, api.js), keep your main application logic in app.js, and manage environment variables with .env. The provided project structure uses `/src/routes` which enhances maintainability and keeps the logic for separate concerns isolated.
Yes, Prisma can be integrated for database interactions (optional). You would install the necessary Prisma dependencies and configure the DATABASE_URL environment variable. The code provides comments and .gitignore entries related to Prisma. It is up to the user to further configure the application to use the database if desired.