This guide provides a step-by-step walkthrough for building a production-ready Node.js application using the Fastify framework to send SMS messages via the Vonage Messages API. We'll cover project setup, API implementation, configuration, error handling, security considerations, and deployment.
By the end of this tutorial, you'll have a simple but robust API endpoint capable of accepting a phone number and message, then using Vonage to deliver that message via SMS.
Project Overview and Goals
Goal: To create a secure and reliable backend service that exposes an API endpoint (/send-sms
) to send SMS messages using Node.js, Fastify, and the Vonage Messages API.
Problem Solved: This provides a foundational building block for applications needing programmatic SMS capabilities, such as sending notifications, alerts, verification codes, or marketing messages.
Technologies Used:
- Node.js: A JavaScript runtime environment for building server-side applications. Chosen for its performance, large ecosystem (npm), and asynchronous nature, suitable for I/O-bound tasks like API calls.
- Fastify: A high-performance, low-overhead web framework for Node.js. Chosen for its speed, developer-friendly experience, built-in validation, and robust plugin architecture.
- Vonage Messages API: A unified API from Vonage for sending messages across various channels (SMS, MMS, WhatsApp, etc.). Chosen for its reliability, global reach, and developer tooling. We specifically use the
@vonage/server-sdk
for Node.js integration. - dotenv: A module to load environment variables from a
.env
file intoprocess.env
, crucial for managing sensitive credentials securely.
System Architecture:
+-----------+ +-----------------+ +----------------+ +-----------------+
| Client | ----> | Fastify API | ---> | Vonage SDK | ---> | Vonage Messages |
| (e.g. curl| | (Node.js Server)| | (@vonage/ | | API |
| Postman) | | | | server-sdk) | | |
+-----------+ +-----------------+ +----------------+ +-----------------+
|
|
v
+---------------+
| SMS Recipient |
+---------------+
(Note: ASCII diagrams may not render perfectly everywhere. Consider using an image for formal documentation.)
Prerequisites:
- Node.js and npm (or yarn): Installed on your system. Download from nodejs.org.
- Vonage API Account: Sign up for free at Vonage API Dashboard. You'll receive some free credits for testing.
- Vonage Application ID and Private Key: Generated from the Vonage Dashboard.
- Vonage Phone Number: Purchased or linked within your Vonage account. This will be the sender number.
- (Optional) Whitelisted Test Numbers: If your Vonage account is in trial/demo mode, you must add recipient phone numbers to a whitelist in your dashboard settings.
- Basic Terminal/Command Line Knowledge: Familiarity with navigating directories and running commands.
1. Setting up the project
Let's initialize our Node.js project and install the necessary dependencies.
-
Create Project Directory: Open your terminal and create a new directory for your project, then navigate into it.
mkdir vonage-fastify-sms cd vonage-fastify-sms
-
Initialize Node.js Project: This creates a
package.json
file to manage your project's metadata and dependencies.npm init -y
-
Install Dependencies: We need Fastify for the web server, the Vonage Server SDK to interact with the API, and
dotenv
for environment variable management.npm install fastify @vonage/server-sdk dotenv
-
Create Project Files: Create the main application file and a file for environment variables.
touch index.js .env .gitignore
-
Configure
.gitignore
: Prevent sensitive files likenode_modules
and.env
from being committed to version control. Add the following lines to your.gitignore
file:# .gitignore node_modules .env *.log private.key # If you store it directly in the project root
-
Set Up Environment Variables (
.env
): Create a.env
file in your project root. This file will store your Vonage credentials and application settings securely. Never commit this file to Git.VONAGE_APPLICATION_ID
: Your Vonage application's unique ID. (See Section 4 for how to get this).VONAGE_PRIVATE_KEY_PATH
: The path to theprivate.key
file downloaded when creating your Vonage application. It's recommended to store this securely outside your main project directory in production, but for simplicity here, you might place it in the project root (ensure it's in.gitignore
).VONAGE_FROM_NUMBER
: The Vonage virtual phone number you purchased or linked, which will appear as the sender ID for the SMS. Use E.164 format (e.g.,14155550100
).PORT
: The port number your Fastify server will listen on (e.g.,3000
).
# .env - DO NOT COMMIT THIS FILE VONAGE_APPLICATION_ID=YOUR_VONAGE_APPLICATION_ID VONAGE_PRIVATE_KEY_PATH=./private.key # Or the actual path to your key VONAGE_FROM_NUMBER=YOUR_VONAGE_PHONE_NUMBER PORT=3000
Remember to replace the placeholder values with your actual credentials.
Project Structure:
Your basic project structure should now look like this:
vonage-fastify-sms/
├── .env # Environment variables (ignored by Git)
├── .gitignore # Specifies intentionally untracked files
├── index.js # Main application logic
├── node_modules/ # Project dependencies (ignored by Git)
├── package.json # Project metadata and dependencies
├── package-lock.json # Records exact dependency versions
└── private.key # Your Vonage private key (if placed here, ensure ignored by Git)
Architectural Decisions:
- Fastify: Chosen over Express for its focus on performance and developer experience, particularly its built-in schema validation which simplifies request handling.
- Environment Variables: Using
dotenv
separates configuration and secrets from code, following the twelve-factor app methodology, enhancing security and portability. - Vonage Messages API: Preferred over the older SMS API as it's Vonage's strategic direction, offering multi-channel capabilities and JWT authentication (though we use App ID/Private Key here for simplicity with the SDK).
2. Implementing core functionality (Sending SMS)
Now, let's write the core logic to interact with the Vonage SDK.
-
Initialize Dependencies in
index.js
: Openindex.js
and require the necessary modules. Load environment variables usingdotenv
right at the top. Initialize Fastify and the Vonage SDK.// index.js 'use strict'; // Load environment variables from .env file require('dotenv').config(); // Import dependencies const Fastify = require('fastify'); const { Vonage } = require('@vonage/server-sdk'); // Configuration check - Ensure essential variables are set if (!process.env.VONAGE_APPLICATION_ID || !process.env.VONAGE_PRIVATE_KEY_PATH || !process.env.VONAGE_FROM_NUMBER) { console.error('Error: Missing required Vonage environment variables.'); process.exit(1); // Exit if configuration is incomplete } // Initialize Vonage SDK // Ensure the path in VONAGE_PRIVATE_KEY_PATH is correct relative to where you run the script const vonage = new Vonage({ applicationId: process.env.VONAGE_APPLICATION_ID, privateKey: process.env.VONAGE_PRIVATE_KEY_PATH, }); // Initialize Fastify server with basic logging enabled const fastify = Fastify({ logger: true, // Enable built-in Pino logger }); const PORT = process.env.PORT || 3000; const FROM_NUMBER = process.env.VONAGE_FROM_NUMBER; // --- API routes will go here --- // Start the server const start = async () => { try { await fastify.listen({ port: PORT, host: '0.0.0.0' }); // Listen on all network interfaces fastify.log.info(`Server listening on port ${PORT}`); } catch (err) { fastify.log.error(err); process.exit(1); } }; start(); // Export for testing purposes if needed // module.exports = { fastify, sendSms }; // Adjust exports as necessary
- We immediately check for essential environment variables to prevent runtime errors later.
- The Vonage SDK is initialized outside any request handlers. This is crucial for performance, as initialization only happens once when the server starts.
- Fastify is initialized with its logger enabled (
logger: true
).
-
Create the
sendSms
Function: It's good practice to encapsulate the SMS sending logic in its own function. This makes the route handler cleaner and the logic reusable. Add this function before the// --- API routes will go here ---
comment inindex.js
.// index.js (continued) /** * Sends an SMS message using the Vonage Messages API. * @param {string} to - The recipient's phone number (E.164 format recommended). * @param {string} text - The message content. * @returns {Promise<object>} - Resolves with the Vonage API response on success. * @throws {Error} - Rejects with an error object on failure. */ async function sendSms(to, text) { // Use the fastify instance logger if available (e.g., during requests), // otherwise fallback to console for scenarios like testing where fastify might not be fully initialized globally. const logger = fastify.log || console; logger.info(`Attempting to send SMS to ${to}`); try { const response = await vonage.messages.send({ message_type: ""text"", // Use standard double quotes to: to, from: FROM_NUMBER, // Use the configured Vonage number channel: ""sms"", // Use standard double quotes text: text, }); logger.info(`Message sent successfully to ${to}. Message UUID: ${response.message_uuid}`); return response; // Return the full response object from Vonage } catch (err) { // Log detailed error information if available const errorMessage = err.response?.data?.title || err.message || 'Unknown error sending SMS'; const errorDetail = err.response?.data?.detail || ''; logger.error(`Error sending SMS to ${to}: ${errorMessage} ${errorDetail}`, err); // Re-throw a more structured error if needed, or just the original error // For simplicity, we re-throw the original error object which contains details throw err; } } // --- API routes will go here ---
- This
async
function takes the recipient number (to
) and message text (text
) as arguments. - It uses
vonage.messages.send
, providing the required parameters. - It uses a
try...catch
block for error handling. If thevonage.messages.send
promise rejects, the error is caught, logged with details, and re-thrown to be handled by the calling code (our API route handler). - We log both success (including the
message_uuid
for tracking) and failure events.
- This
3. Building a complete API layer
Now, let's create the Fastify route that will expose our SMS sending functionality.
-
Define the API Route (
/send-sms
): Add the following route definition inindex.js
, replacing the// --- API routes will go here ---
comment.// index.js (continued) // --- API Routes --- // Define the schema for the request body and response const sendSmsSchema = { body: { type: 'object', required: ['to', 'text'], // Both 'to' and 'text' are mandatory properties: { to: { type: 'string', description: 'Recipient phone number (E.164 format recommended, e.g., +14155550100)', // Basic pattern for E.164-like numbers, requires '+' and digits. // Matches the entire string. pattern: '^\\+?[1-9]\\d{1,14}$', // Corrected: Added end anchor '$' }, text: { type: 'string', description: 'The content of the SMS message', minLength: 1, // Ensure message is not empty maxLength: 1600 // Standard SMS limits apply (concatenation handled by carriers) } } }, response: { 200: { // Successful response schema type: 'object', properties: { success: { type: 'boolean' }, message_uuid: { type: 'string' }, detail: { type: 'string' } } }, 400: { // Bad request (validation failed) schema type: 'object', properties: { success: { type: 'boolean', default: false }, // Explicitly false for errors error: { type: 'string' }, message: { type: 'string' } } }, 500: { // Server error (e.g., Vonage API error) schema type: 'object', properties: { success: { type: 'boolean', default: false }, // Explicitly false for errors error: { type: 'string' }, message: { type: 'string' }, detail: { type: 'string', nullable: true } // Optional detailed error message } } } }; // POST route to send an SMS fastify.post('/send-sms', { schema: sendSmsSchema }, async (request, reply) => { const { to, text } = request.body; // Destructure validated body try { // Ensure sendSms function is defined before this route const vonageResponse = await sendSms(to, text); // Send successful response reply.code(200).send({ success: true, message_uuid: vonageResponse.message_uuid, detail: `SMS submitted successfully to ${to}.` }); } catch (error) { // Handle errors from the sendSms function or Vonage API request.log.error({ err: error }, 'SMS sending failed'); // Log the error object // Provide a generic error message, potentially including Vonage-specific details if safe const vonageErrorTitle = error.response?.data?.title; const vonageErrorDetail = error.response?.data?.detail; // Determine appropriate status code (500 for server/API issues, potentially others) // For simplicity, using 500 for any failure during SMS sending. reply.code(500).send({ success: false, error: 'Failed to send SMS', message: vonageErrorTitle || 'An internal server error occurred.', detail: vonageErrorDetail // Include Vonage detail if available }); } }); // --- Start the server (already defined below) ---
- Schema Validation: We define a
schema
object for the route. Fastify automatically validates incoming request bodies againstschema.body
. If validation fails, Fastify sends a 400 Bad Request response before our handler even runs. This ensures we only process valid data.required: ['to', 'text']
: Both fields are mandatory.properties
: Defines the expected type and basic constraints (likepattern
for phone numbers,minLength
/maxLength
for text).response
: Defines the expected structure for different HTTP status codes (200, 400, 500). This helps with documentation and consistency.
- Route Handler: The
async (request, reply)
function is executed only if the request body passes validation.- It extracts the validated
to
andtext
fromrequest.body
. - It calls our
sendSms
function. - Success: If
sendSms
resolves, it sends a 200 OK response with themessage_uuid
received from Vonage. - Failure: If
sendSms
throws an error (e.g., Vonage API error, invalid credentials), thecatch
block executes. It logs the error and sends a 500 Internal Server Error response with relevant error details (safely extracted from the Vonage error if available).
- It extracts the validated
- Authentication: This basic example lacks authentication. In a real application, you would add authentication/authorization middleware (e.g., checking for a valid API key in headers using
fastify.addHook('preHandler', ...)
).
- Schema Validation: We define a
-
Testing the API Endpoint: Once the server is running (
node index.js
), you can test the endpoint usingcurl
or a tool like Postman.Using
curl
: ReplaceYOUR_WHITELISTED_NUMBER
with a phone number you have added to your Vonage test numbers list (if in trial mode) or any valid number if your account is funded. Use the E.164 format (e.g.,+14155550100
).curl -X POST http://localhost:3000/send-sms \ -H ""Content-Type: application/json"" \ -d '{ ""to"": ""+YOUR_WHITELISTED_NUMBER"", ""text"": ""Hello from Fastify and Vonage! This is a test message."" }'
Expected Successful Response (JSON):
{ ""success"": true, ""message_uuid"": ""aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"", ""detail"": ""SMS submitted successfully to +YOUR_WHITELISTED_NUMBER."" }
Example Failed Validation Response (e.g., missing
text
): (Fastify handles this automatically based on the schema){ ""statusCode"": 400, ""error"": ""Bad Request"", ""message"": ""body should have required property 'text'"" }
Example Vonage API Error Response (e.g., invalid credentials):
{ ""success"": false, ""error"": ""Failed to send SMS"", ""message"": ""Unauthorized"", ""detail"": ""Please check your Application ID and Private Key."" }
4. Integrating with necessary third-party services (Vonage)
Properly configuring your Vonage account is critical.
- Sign Up/Log In: Go to the Vonage API Dashboard.
- Check API Settings:
- Navigate to API Settings in the left-hand menu.
- Scroll down to SMS Settings.
- Ensure that ""Default SMS Setting"" is set to Messages API. If it's set to ""SMS API"", switch it to ""Messages API"" and click Save changes. This ensures the SDK uses the correct backend API. (Ensure this setting is configured in your Vonage dashboard as described.)
- Create a Vonage Application:
- Navigate to Applications > + Create a new application.
- Give your application a name (e.g.,
""Fastify SMS Sender""
). - Click Generate public and private key. This will automatically download the
private.key
file. Save this file securely. Note its location – you'll need the path for theVONAGE_PRIVATE_KEY_PATH
environment variable. Do not lose this key; Vonage does not store it. - Enable the Messages capability.
- You can leave the Inbound URL and Status URL blank for sending only. If you wanted to receive SMS or get delivery receipts, you would need to provide publicly accessible webhook URLs here (often requiring a tool like
ngrok
for local development). - Click Generate application.
- You will be shown the Application ID. Copy this value and use it for the
VONAGE_APPLICATION_ID
environment variable.
- Link a Phone Number:
- Go to Numbers > Your numbers.
- If you don't have a number, go to Buy numbers and purchase an SMS-capable number in your desired country.
- Once you have a number, find it under Your numbers. Click the Manage button (or gear icon) next to it.
- In the Forwarding section (or similar settings area), find the Applications dropdown or link.
- Select the application you just created (
""Fastify SMS Sender""
) from the dropdown list. - Click Save.
- Copy this phone number (in E.164 format, e.g.,
+14155550100
) and use it for theVONAGE_FROM_NUMBER
environment variable.
- (Trial Accounts Only) Whitelist Test Numbers:
- If your account hasn't been topped up with credit, you are in trial/demo mode.
- Navigate to Account > Settings (or look for ""Test Numbers"" / ""Whitelisted Numbers"").
- Add the phone number(s) you intend to send test messages to. You will usually need to verify them via an SMS or call code sent by Vonage.
- You can only send messages to these verified numbers while in trial mode.
- Secure Credential Handling:
- Store your
VONAGE_APPLICATION_ID
andVONAGE_FROM_NUMBER
in the.env
file. - Store your
private.key
file securely. Placing it in the project root is acceptable for local development if listed in.gitignore
, but for production, consider storing it outside the application directory with restricted file permissions or using a secrets management system. SetVONAGE_PRIVATE_KEY_PATH
in.env
to the correct path. - Ensure
.env
andprivate.key
are included in your.gitignore
file.
- Store your
5. Implementing proper error handling, logging, and retry mechanisms
We've already incorporated basic error handling and logging. Let's refine it.
- Consistent Error Handling: Our API route uses
try...catch
to handle errors originating from thesendSms
function (which includes errors from the Vonage SDK). It consistently returns a JSON object withsuccess: false
and error details. - Logging: Fastify's built-in Pino logger (
fastify.log
) is used:fastify.log.info
: Logs successful operations (server start, SMS sending attempts, success confirmation).fastify.log.error
: Logs errors during server startup or SMS sending failures, including the error object for detailed debugging.- Log Levels: In production, you might configure Pino to log only
info
and above to reduce noise, while loggingdebug
ortrace
in development. This is configurable when initializing Fastify. - Log Format: Pino logs in JSON format by default, which is excellent for structured logging platforms (like Datadog, Splunk, ELK stack).
- Retry Mechanisms:
-
Vonage: Vonage itself has internal retry mechanisms for delivering SMS messages across carrier networks.
-
Application Level: For transient network errors between your server and the Vonage API, you could implement a retry strategy. A simple approach uses
async-retry
:-
Install:
npm install async-retry
-
Modify
sendSms
function:// index.js (add import at the top) const retry = require('async-retry'); // Modify the sendSms function async function sendSms(to, text) { const logger = fastify.log || console; // Use logger safely logger.info(`Attempting to send SMS to ${to}`); return retry(async (bail, attempt) => { // bail is a function to stop retrying (e.g., for non-recoverable errors) // attempt is the current attempt number logger.info(`Sending SMS attempt ${attempt} to ${to}`); try { // Corrected: Explicitly include parameters const response = await vonage.messages.send({ message_type: ""text"", to: to, from: FROM_NUMBER, channel: ""sms"", text: text, }); logger.info(`Message sent successfully to ${to}. Message UUID: ${response.message_uuid}`); return response; } catch (err) { logger.warn(`Attempt ${attempt} failed for ${to}: ${err.message}`); // Example: Don't retry on specific Vonage errors like 'Unauthorized' (401) // or 'Bad Request' (400) as retrying won't help. if (err.response && (err.response.status === 401 || err.response.status === 400)) { logger.error(`Non-recoverable error (${err.response.status}), stopping retries.`); bail(err); // Stop retrying and throw the error immediately return; // Necessary after bail to exit the current retry attempt function } // For other errors (e.g., network issues, 5xx from Vonage), throw to trigger retry throw err; } }, { retries: 3, // Number of retries (e.g., 3 attempts total) factor: 2, // Exponential backoff factor minTimeout: 1000, // Initial delay 1 second maxTimeout: 5000, // Max delay 5 seconds onRetry: (error, attempt) => { logger.warn(`Retrying SMS to ${to} (attempt ${attempt}) after error: ${error.message}`); } }); }
- This wraps the
vonage.messages.send
call inretry
. It attempts the call up to 3 times with exponential backoff (1s, 2s, 4s delays). - It includes logic (
bail
) to stop retrying for specific errors (like authentication or bad request errors) where retrying is pointless.
-
-
- Testing Error Scenarios:
- Invalid Credentials: Temporarily change
VONAGE_APPLICATION_ID
orVONAGE_PRIVATE_KEY_PATH
in.env
and restart. Send a request; you should get a 500 error with an ""Unauthorized"" message. - Invalid Recipient: Send to a deliberately malformed number (e.g.,
123
). Fastify validation should catch this (400). Send to a valid format but non-existent/non-whitelisted number – observe the Vonage error (might be ""Invalid Recipient"" or ""Non-Whitelisted Destination""). - Network Issues: Simulate network interruption between your server and Vonage (harder locally, but possible with network tools or by temporarily blocking Vonage IPs). Test if the retry logic kicks in.
- Invalid Credentials: Temporarily change
6. Creating a database schema and data layer
For this specific guide focused only on sending an immediate SMS via API call, a database is not strictly necessary.
If you were building features like:
- Storing message history
- Scheduling messages
- Tracking delivery statuses via webhooks
- Managing user contacts
You would need a database (e.g., PostgreSQL, MongoDB) and a data layer (e.g., using an ORM like Prisma or Sequelize). This is beyond the scope of this basic sending guide.
7. Adding security features
Security is paramount when dealing with APIs and sensitive credentials.
- Input Validation and Sanitization:
- Done: Fastify's schema validation (
sendSmsSchema
) enforces types, required fields, and basic patterns forto
andtext
. This prevents many injection-style attacks and ensures data integrity before it reaches our core logic. - Further Steps: For
text
, depending on the use case, you might implement more aggressive sanitization (e.g., stripping HTML tags if the input source is untrusted) using libraries likesanitize-html
.
- Done: Fastify's schema validation (
- Secrets Management:
- Done: Using
.env
and.gitignore
prevents hardcoding credentials and leaking them into version control. - Production: Use environment variables provided by your hosting platform or a dedicated secrets management service (like AWS Secrets Manager, GCP Secret Manager, HashiCorp Vault).
- Done: Using
- Protection Against Common Vulnerabilities:
-
Input Validation: Helps prevent NoSQL injection, XSS (if
text
were rendered elsewhere), etc. -
Rate Limiting: Crucial to prevent abuse and control costs. Use a Fastify plugin like
@fastify/rate-limit
.npm install @fastify/rate-limit
// index.js (near the top, after Fastify init) const rateLimit = require('@fastify/rate-limit'); // Register the plugin (before defining routes) async function registerPlugins() { await fastify.register(rateLimit, { max: 100, // Max requests per windowMs per key timeWindow: '1 minute', // Time window // keyGenerator: function (request) { /* ... custom key logic ... */ } // Optional: Rate limit per IP, API key, etc. }); } // Call this after fastify init, before start() registerPlugins().catch(err => { fastify.log.error('Error registering plugins:', err); process.exit(1); }); // ... rest of the code ...
This adds basic rate limiting (e.g., 100 requests per minute per IP). Configure
max
andtimeWindow
appropriately. You might implement stricter limits specifically on the/send-sms
route. -
Authentication/Authorization: As mentioned, implement API key checks or other mechanisms (
fastify.addHook
) to ensure only authorized clients can use the endpoint.
-
- Security Headers: Consider adding security headers using
@fastify/helmet
for general web security best practices, though less critical for a pure API backend. - Testing for Vulnerabilities:
- Manually test edge cases in input validation (empty strings, overly long messages, invalid characters in phone numbers).
- Use security scanners or penetration testing tools (like OWASP ZAP) against your deployed application.
- Review logs for suspicious activity patterns.
8. Handling special cases relevant to the domain
Sending SMS involves nuances:
- Phone Number Formatting:
- Recommendation: Always aim to store and process numbers in E.164 format (e.g.,
+14155550100
). Vonage generally handles variations, but E.164 is the unambiguous standard. - Validation: Our schema includes a basic pattern (
^\\+?[1-9]\\d{1,14}$
) matching the full string. You could use a more specific library likelibphonenumber-js
for robust parsing and validation if needed.
- Recommendation: Always aim to store and process numbers in E.164 format (e.g.,
- Character Limits and Encoding:
- Standard SMS messages (GSM-7 encoding) are limited to 160 characters.
- Longer messages are split into multiple segments (concatenated SMS), each consuming credits. GSM-7 supports a limited character set.
- Using non-GSM-7 characters (like emojis or Cyrillic letters) switches the encoding to UCS-2, reducing the limit per segment to 70 characters.
- Vonage and carriers handle concatenation, but be mindful of message length for cost and user experience. Our schema allows up to 1600 characters (
maxLength: 1600
) as a rough upper bound, but Vonage/carriers enforce actual segment limits.
- International Sending:
- Sender ID: Rules for the
from
number (Sender ID) vary significantly by country. Some countries require pre-registration, some replace alphanumeric IDs with local numbers, and some prohibit them entirely. Using a purchased Vonage number from the target region often provides the best deliverability. Check Vonage's country-specific guidelines. - Regulations: Be aware of local regulations regarding SMS content, opt-in requirements, and sending times (TCPA in the US, GDPR in Europe, etc.).
- Sender ID: Rules for the
- Deliverability: Factors like carrier filtering, invalid numbers, and regulatory compliance can affect whether an SMS is delivered. Monitoring delivery receipts (requires setting up a Status webhook in your Vonage Application) is important for production systems.
9. Implementing performance optimizations
For this simple API, major optimizations are likely unnecessary, but good practices include:
- SDK Initialization: Done: The Vonage SDK is initialized once at startup, not per request.
- Asynchronous Operations: Done: Node.js and Fastify are inherently asynchronous. Using
async/await
ensures the server isn't blocked during the API call to Vonage. - Logging: Using an efficient logger like Pino (Fastify's default) has minimal performance impact compared to
console.log
.