code examples
code examples
Send SMS with Node.js, Express, and Vonage Messages API
Complete guide for building a production-ready Node.js application using Express to send SMS messages via Vonage Messages API
Send SMS with Node.js, Express, and Vonage Messages API
This guide provides a complete walkthrough for building a production-ready Node.js application using the Express framework to send SMS messages via the Vonage Messages API. We'll cover everything from project setup and configuration to core functionality, error handling, security, and deployment.
By the end of this guide, you will have a simple but robust API endpoint capable of accepting a phone number and message, and reliably sending an SMS using Vonage.
Project Overview and Goals
Goal: To create a simple REST API endpoint using Node.js and Express that sends an SMS message to a specified recipient number using the Vonage Messages API.
Problem Solved: Provides a reusable, server-side component for applications needing to send transactional or notification SMS messages programmatically.
Technologies Used:
- Node.js: A JavaScript runtime environment for building server-side applications. Chosen for its non-blocking I/O, large ecosystem (npm), and JavaScript ubiquity.
- Express: A minimal and flexible Node.js web application framework. Chosen for its simplicity, robustness, and widespread adoption for building APIs.
- Vonage Messages API: A powerful API for sending messages across various channels (SMS, MMS, WhatsApp, etc.). Chosen for its reliability, global reach, and comprehensive features for SMS communication. We specifically use the Messages API over the older SMS API for its modern features and multi-channel capabilities.
dotenv: A zero-dependency module that loads environment variables from a.envfile intoprocess.env. Chosen for securely managing API keys and configuration outside of the codebase.- Vonage Node.js Server SDK (
@vonage/server-sdk): The official Vonage SDK simplifies interaction with the Vonage APIs.
System Architecture:
+-------------+ +------------------------+ +---------------------+ +-------------------+
| User/Client | ---> | Node.js/Express Server | ---> | Vonage Messages API | ---> | Recipient's Phone |
| (e.g., curl,| | (API Endpoint /send-sms)| | (Sends SMS) | | (Receives SMS) |
| Postman) | +------------------------+ +---------------------+ +-------------------+
+-------------+ |
| Uses Vonage SDK
| Reads Credentials (.env)
+------------------------+
| Vonage SDK |
| dotenv |
+------------------------+Prerequisites:
- Node.js and npm (or yarn): Installed on your development machine. (Download Node.js)
- Vonage API Account: Sign up for free. (Vonage Sign Up)
- Vonage Virtual Number: You need at least one Vonage phone number capable of sending SMS. You can purchase one from the Vonage dashboard.
- Vonage Application: You'll need to create a Vonage Application within your dashboard to obtain an Application ID and generate a private key.
- (Optional)
curlor Postman: For testing the API endpoint.
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.
bashmkdir vonage-sms-sender cd vonage-sms-sender -
Initialize npm Project: This creates a
package.jsonfile to manage your project's dependencies and scripts.bashnpm init -y -
Install Dependencies: We need
expressfor the web server,@vonage/server-sdkto interact with the Vonage API, anddotenvto manage environment variables.bashnpm install express @vonage/server-sdk dotenv --saveexpress: Web framework.@vonage/server-sdk: Vonage API client library.dotenv: Loads environment variables from.env.
-
Create Project Structure: Create the main application file and a file for environment variables.
bashtouch index.js .env .env.example .gitignoreindex.js: Main application code..env: Stores your secret API credentials (will be ignored by Git)..env.example: A template showing required environment variables (safe to commit)..gitignore: Specifies files Git should ignore.
-
Configure
.gitignore: It's crucial never to commit your secrets. Add.envandnode_modulesto your.gitignorefile:text# .gitignore # Dependencies node_modules/ # Environment variables .env # Optional keys private.key # Logs npm-debug.log* yarn-debug.log* yarn-error.log* # Optional editor directories .vscode/ .idea/ -
Set up Environment Variables (
.env.example): Define the structure of your required environment variables in.env.example. This serves as a template for anyone setting up the project.dotenv# .env.example # Port for the Express server PORT=3000 # Vonage API Credentials # Found in your Vonage Application settings VONAGE_APPLICATION_ID=YOUR_VONAGE_APPLICATION_ID # Provide EITHER the path to the private key file OR the key content directly # Path is generally preferred for local development VONAGE_PRIVATE_KEY_PATH=./private.key # Content is useful for environments where mounting files is hard (e.g., some PaaS) # VONAGE_PRIVATE_KEY_CONTENT="-----BEGIN PRIVATE KEY-----\nYOUR_KEY_CONTENT...\n-----END PRIVATE KEY-----" # Vonage virtual number to send SMS from (E.164 format recommended) VONAGE_FROM_NUMBER=YOUR_VONAGE_VIRTUAL_NUMBER -
Populate
.env(DO NOT COMMIT THIS FILE): Copy.env.exampleto.envand fill in your actual credentials. You'll obtain these in the "Integrating with Vonage" section. Use eitherVONAGE_PRIVATE_KEY_PATHorVONAGE_PRIVATE_KEY_CONTENT.bashcp .env.example .envEdit
.envand replace the placeholder values. Make sure theprivate.keyfile exists at the specified path if usingVONAGE_PRIVATE_KEY_PATH.
2. Implementing Core Functionality (Sending SMS)
We'll encapsulate the Vonage SMS sending logic within a reusable function.
-
Edit
index.js: Set up the Express server, import modules, and initialize the Vonage client.javascript// index.js require('dotenv').config(); // Load environment variables from .env file const express = require('express'); const { Vonage } = require('@vonage/server-sdk'); // --- Configuration --- const app = express(); const port = process.env.PORT || 3000; // --- Vonage Client Initialization --- let vonage; try { // Ensure required base environment variables are set if (!process.env.VONAGE_APPLICATION_ID || !process.env.VONAGE_FROM_NUMBER) { throw new Error('Missing VONAGE_APPLICATION_ID or VONAGE_FROM_NUMBER in .env'); } // Determine private key source (path or content) let privateKey; if (process.env.VONAGE_PRIVATE_KEY_CONTENT) { privateKey = Buffer.from(process.env.VONAGE_PRIVATE_KEY_CONTENT, 'utf8'); console.log('Initializing Vonage client using private key content from environment variable.'); } else if (process.env.VONAGE_PRIVATE_KEY_PATH) { privateKey = process.env.VONAGE_PRIVATE_KEY_PATH; console.log('Initializing Vonage client using private key path from environment variable.'); } else { throw new Error('Missing Vonage private key. Set either VONAGE_PRIVATE_KEY_PATH or VONAGE_PRIVATE_KEY_CONTENT environment variable.'); } // Initialize Vonage SDK vonage = new Vonage({ applicationId: process.env.VONAGE_APPLICATION_ID, privateKey: privateKey }); console.log('Vonage client initialized successfully.'); } catch (error) { console.error('Error initializing Vonage client:', error.message); console.error('Please check your .env file, ensure required variables are set, and the private key path/content is correct.'); process.exit(1); // Exit if configuration is invalid } // --- Middleware --- app.use(express.json()); // Parse JSON request bodies app.use(express.urlencoded({ extended: true })); // Parse URL-encoded request bodies // --- Core SMS Sending Function --- /** * Sends an SMS message using the Vonage Messages API. * @param {string} to - The recipient phone number (E.164 format recommended). * @param {string} text - The message content. * @returns {Promise<object>} - A promise that resolves with the Vonage API response. * @throws {Error} - Throws an error if sending fails. */ async function sendSms(to, text) { console.log(`Attempting to send SMS to: ${to}`); try { const resp = await vonage.messages.send({ channel: 'sms', message_type: 'text', to: to, from: process.env.VONAGE_FROM_NUMBER, // Your Vonage virtual number text: text }); console.log(`SMS submitted successfully to ${to}. Message UUID: ${resp.message_uuid}`); return resp; // Contains message_uuid } catch (err) { console.error(`Error sending SMS to ${to}:`, err.response ? err.response.data : err.message); // Rethrow a more specific error for the API layer to handle throw new Error(`Failed to send SMS. Vonage API Error: ${err.response ? JSON.stringify(err.response.data) : err.message}`); } } // --- API Routes (Defined in next section) --- // ... // --- Server Start --- app.listen(port, () => { console.log(`Server listening on http://localhost:${port}`); }); module.exports = { app, sendSms }; // Export for potential testingExplanation:
require('dotenv').config(): Loads variables from.envintoprocess.env.express(): Creates an Express application instance.- Vonage Client Initialization:
- Checks for required
VONAGE_APPLICATION_IDandVONAGE_FROM_NUMBER. - Checks for either
VONAGE_PRIVATE_KEY_CONTENTorVONAGE_PRIVATE_KEY_PATH. - If
VONAGE_PRIVATE_KEY_CONTENTis found, it's used directly (as a Buffer). - Otherwise,
VONAGE_PRIVATE_KEY_PATHis used (the SDK reads the file). - Initializes
new Vonage(...)with the application ID and the determined private key (either content or path). - Includes robust error handling for missing configuration or initialization failures.
- Checks for required
express.json(),express.urlencoded(): Middleware to parse incoming request bodies.sendSms(to, text): Anasyncfunction that usesvonage.messages.sendto send the SMS.channel: 'sms': Specifies SMS channel.message_type: 'text': Specifies plain text message.to: Recipient number.from: Your Vonage number (from.env).text: The message content.
- Error Handling (sendSms): Uses a
try...catchblock. Logs success or failure. If Vonage returns an error (e.g.,err.response.data), it logs the detailed error from the API. It rethrows a standardized error for the API route handler.
3. Building a Complete API Layer
Now, let's create the Express API endpoint to trigger the SMS sending function.
-
Add API Route to
index.js: Add the following code block before theapp.listencall inindex.js.API Endpoint: POST /send-sms
-
Description: Sends an SMS message.
-
Access: Public (consider adding authentication in production).
-
Request Body: JSON object containing:
to(string): The recipient phone number (E.164 format recommended).message(string): The text message content.
json{ "to": "+1234567890", "message": "Hello from Vonage!" } -
Success Response (200 OK):
json{ "success": true, "messageId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", "detail": "SMS submitted successfully." } -
Error Response (400 Bad Request or 500 Internal Server Error):
json{ "success": false, "error": "Error description (e.g., Missing required fields, Invalid phone number format, Failed to send SMS)." }
javascript// index.js (continued) // --- API Routes --- app.post('/send-sms', async (req, res) => { const { to, message } = req.body; // Basic Input Validation if (!to || !message) { console.warn('Validation failed: Missing `to` or `message` in request body.'); return res.status(400).json({ success: false, error: 'Missing required fields: `to` and `message`.' }); } // Basic Phone Number Format Check (Simple Example - NOT PRODUCTION READY) // WARNING: This regex is very basic and might not cover all valid E.164 formats or edge cases. // For production, use a dedicated library like `libphonenumber-js` for robust validation. if (!/^\+?[1-9]\d{1,14}$/.test(to)) { console.warn(`Validation failed: Invalid phone number format: ${to}`); return res.status(400).json({ success: false, error: 'Invalid phone number format. Use E.164 format (e.g., +1234567890).' }); } try { const result = await sendSms(to, message); res.status(200).json({ success: true, messageId: result.message_uuid, detail: "SMS submitted successfully." }); } catch (error) { // Error already logged in sendSms function res.status(500).json({ success: false, error: error.message || "An internal server error occurred while sending SMS." }); } }); // Add a simple health check endpoint app.get('/health', (req, res) => { res.status(200).json({ status: 'UP' }); }); // --- Server Start --- // ... (rest of the file)Explanation:
app.post('/send-sms', ...): Defines a route that listens for POST requests on the/send-smspath.- Input Validation: Checks if
toandmessageare present in the JSON request body (req.body). Returns a 400 Bad Request error if missing. Includes a very basic regex check for phone number format (E.164 recommended). Crucially, this regex is insufficient for production use. A dedicated library likegoogle-libphonenumberorlibphonenumber-jsshould be used for proper validation in a real-world application. - Call
sendSms: Calls the core function with the validatedtoandmessage. - Response Handling:
- On success (
tryblock): Returns a 200 OK status withsuccess: trueand themessage_uuidreceived from Vonage. - On failure (
catchblock): Returns a 500 Internal Server Error status withsuccess: falseand the error message generated bysendSms.
- On success (
-
4. Integrating with Vonage
This involves creating a Vonage Application and obtaining the necessary credentials.
-
Log in to Vonage Dashboard: Go to https://dashboard.nexmo.com/ and log in.
-
Navigate to Applications: In the left-hand navigation menu, go to "Applications" under "Build & Manage".
-
Create a New Application:
- Click the "+ Create a new application" button.
- Give your application a name (e.g., "Node SMS Sender App").
- Click "Generate public and private key". This will automatically download the
private.keyfile. Save this file securely in the root of your project directory (the same place asindex.js). Do not commit this file to Git. - Enable the Messages capability.
- You'll see fields for "Inbound URL" and "Status URL". For sending SMS only, these are not strictly required to be functional endpoints, but the Vonage platform expects URLs. You can enter placeholder URLs like
https://example.com/webhooks/inboundandhttps://example.com/webhooks/status. If you later want to receive status updates or inbound messages, you'll need to update these with real endpoints. - Click "Generate new application".
-
Get Application ID: On the application details page, copy the Application ID.
-
Link Your Vonage Number:
- Scroll down to the "Link virtual numbers" section on the application details page.
- Find the Vonage number you want to send SMS from.
- Click the "Link" button next to it. This authorizes the application to use this number.
-
Update
.envFile: Open your.envfile and paste the credentials:dotenv# .env (Your actual values) PORT=3000 VONAGE_APPLICATION_ID=YOUR_COPIED_APPLICATION_ID # Use EITHER VONAGE_PRIVATE_KEY_PATH OR VONAGE_PRIVATE_KEY_CONTENT VONAGE_PRIVATE_KEY_PATH=./private.key # Ensure this matches the location of your downloaded key # VONAGE_PRIVATE_KEY_CONTENT="PASTE_KEY_CONTENT_HERE" # Alternative if not using path VONAGE_FROM_NUMBER=YOUR_LINKED_VONAGE_NUMBER # e.g., +14155550100(Ensure only one private key variable is uncommented and correctly set)
5. Implementing Error Handling and Logging
We've already incorporated basic error handling and logging:
- Initialization Errors: The code checks for missing environment variables and errors during Vonage client setup, exiting gracefully if critical configuration is missing.
- API Request Validation: The
/send-smsendpoint validates required input (to,message) and returns specific 400 errors. Includes a basic (non-production) phone format check. - Vonage API Errors: The
sendSmsfunction catches errors from the Vonage SDK. It logs detailed error information (err.response.dataif available) and returns a user-friendly error message in the API response (500 status). - Logging: Uses
console.log,console.warn, andconsole.errorfor different levels of information.
Further Enhancements (Production Considerations):
- Structured Logging: Use a library like
winstonorpinofor structured JSON logging, which is easier to parse and analyze in production monitoring tools (e.g., Datadog, Splunk). - Centralized Error Tracking: Integrate with services like Sentry or Bugsnag to automatically capture and report exceptions.
- Retry Mechanisms: For transient network issues or temporary Vonage API unavailability, implement a retry strategy (e.g., exponential backoff) using libraries like
async-retry. However, for SMS sending, be cautious about retrying automatically, as it could lead to duplicate messages if the initial request succeeded but the response was lost. It's often better to rely on Vonage's internal retries or flag the message for manual review/retry. - Robust Phone Validation: Replace the basic regex check with
libphonenumber-jsorgoogle-libphonenumber.
6. Creating a Database Schema and Data Layer
This specific application focuses solely on sending an SMS via an API call and does not inherently require a database.
If you needed to store message history, track delivery statuses via webhooks, manage user data, or queue messages, you would introduce a database (e.g., PostgreSQL, MongoDB) and a data access 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, especially when handling API keys and potentially sensitive data.
-
Secrets Management:
.envand.gitignore: As implemented, sensitive credentials are stored in.env, which is listed in.gitignoreto prevent accidental commits.- Private Key: The
private.keyfile itself (if using the path method) should also be kept secure and listed in.gitignore. Access permissions on the server should restrict read access to the application user only. - Production Environments: Use dedicated secrets management solutions provided by your cloud provider (e.g., AWS Secrets Manager, Google Secret Manager, Azure Key Vault) or environment variable injection instead of
.envfiles. TheVONAGE_PRIVATE_KEY_CONTENTvariable is suitable for these scenarios.
-
Input Validation and Sanitization:
- Implemented: Basic validation checks for the presence of
toandmessage. Basic format check forto(needs improvement for production). - Enhancement: Use robust libraries (like
google-libphonenumber/libphonenumber-jsfor phone numbers,DOMPurifyif message content could be HTML, orvalidator.js) for stricter validation and sanitization to prevent injection attacks or malformed data reaching the Vonage API.
- Implemented: Basic validation checks for the presence of
-
Rate Limiting:
- Not Implemented: Protect your API endpoint from abuse or accidental loops by implementing rate limiting.
- How: Use middleware like
express-rate-limit.
bashnpm install express-rate-limitjavascript// Add near the top of index.js const rateLimit = require('express-rate-limit'); const limiter = rateLimit({ windowMs: 15 * 60 * 1000, // 15 minutes max: 100, // Limit each IP to 100 requests per windowMs standardHeaders: true, // Return rate limit info in the `RateLimit-*` headers legacyHeaders: false, // Disable the `X-RateLimit-*` headers message: 'Too many requests from this IP, please try again after 15 minutes' }); // Apply the rate limiting middleware to API routes // Place this before your API route definitions app.use('/send-sms', limiter); -
Authentication/Authorization:
- Not Implemented: The current endpoint is public. In a real application, you would protect it.
- How: Implement API key checking, JWT (JSON Web Tokens), OAuth, or session-based authentication depending on your application's needs. Add middleware to verify credentials before allowing access to the
/send-smsroute.
-
HTTPS: Always use HTTPS in production to encrypt data in transit. Use a reverse proxy like Nginx or Caddy, or platform services (like AWS ELB, Heroku) to handle TLS termination.
-
Helmet: Use the
helmetmiddleware for Express to set various HTTP headers that improve security (e.g., Content-Security-Policy, X-Frame-Options).bashnpm install helmetjavascript// Add near the top of index.js const helmet = require('helmet'); app.use(helmet());
8. Handling Special Cases
- International Phone Numbers:
- Recommendation: Always use the E.164 format (e.g.,
+14155550100,+442071838750) for thetonumber. Vonage generally expects this format for reliable international delivery. - Library: Use
google-libphonenumberorlibphonenumber-jsto parse and validate numbers from various formats and convert them to E.164. The current basic regex is insufficient.
- Recommendation: Always use the E.164 format (e.g.,
- SMS Character Limits & Encoding:
- Standard SMS messages (GSM-7 encoding) are limited to 160 characters.
- Messages with non-standard characters (like emojis or certain accented letters) use UCS-2 encoding, reducing the limit to 70 characters per segment.
- Longer messages are split into multiple segments (concatenated SMS). Vonage handles this automatically, but each segment is billed separately. Be mindful of message length to manage costs. You might add validation to limit message length on your server.
- Vonage Trial Account Limitations:
- Whitelisting: If using a Vonage trial account (before adding payment details), you can typically only send SMS messages to phone numbers that you have verified and added to your "test numbers" list in the Vonage dashboard (Settings -> Test numbers). Attempts to send to other numbers will result in an error (often "Non-Whitelisted Destination").
- Branding: Trial messages might have "[FREE SMS DEMO, TEST MESSAGE]" prepended.
9. Implementing Performance Optimizations
For this simple API, performance is unlikely to be a major issue unless handling extremely high volume.
-
Asynchronous Operations: Node.js and the Vonage SDK are inherently asynchronous, preventing the server from blocking while waiting for the API call to complete. This is the most significant performance aspect already built-in.
-
Connection Pooling: The Vonage SDK likely handles underlying HTTP connection management efficiently.
-
Payload Size: Keep request/response payloads small. We are already doing this.
-
High Volume Considerations:
- Queuing: If you need to send thousands of messages rapidly, pushing requests onto a message queue (like RabbitMQ or Redis BullMQ) and having separate worker processes handle the actual sending via
sendSmscan decouple the API response from the sending process and improve API responsiveness. - Concurrency Limits: Be mindful of Vonage API rate limits (check their documentation). Implement application-level throttling if necessary to avoid hitting these limits.
- Queuing: If you need to send thousands of messages rapidly, pushing requests onto a message queue (like RabbitMQ or Redis BullMQ) and having separate worker processes handle the actual sending via
-
Load Testing: Use tools like
k6,artillery, orApacheBench (ab)to simulate traffic and identify potential bottlenecks under load.bash# Example using ab (ApacheBench) - sends 100 requests, 10 concurrently # Make sure the server is running! # Create a file payload.json: {"to": "+1YOUR_TEST_NUMBER", "message": "Load test"} ab -n 100 -c 10 -p payload.json -T 'application/json' http://localhost:3000/send-sms
10. Adding Monitoring, Observability, and Analytics
Ensure you can monitor the health and performance of your service.
- Health Checks:
- Implemented: A basic
/healthendpoint returning{"status": "UP"}. Monitoring systems (like Kubernetes liveness/readiness probes, AWS health checks, or uptime checkers) can poll this endpoint.
- Implemented: A basic
- Logging: (As discussed in Section 5) Centralized logging is key for observing behavior and diagnosing issues. Consider structured logging for production.
- Metrics:
- Track Key Indicators: Use libraries like
prom-client(for Prometheus) or integrate with APM (Application Performance Management) services (Datadog, New Relic). - Metrics to Track:
- Request rate and latency for
/send-sms. - Error rates (overall and specific error types).
- Number of successful SMS submissions (
sendSmscalls). - Number of failed SMS submissions.
- Vonage API call latency (if measurable via SDK or wrappers).
- Request rate and latency for
- Track Key Indicators: Use libraries like
- Error Tracking: (As discussed in Section 5) Services like Sentry provide real-time error alerting and aggregation.
- Vonage Dashboard: Utilize the Vonage dashboard itself for analytics on message delivery rates, costs, and usage patterns.
- Status Webhooks: For production, implement the
status_urlwebhook specified when creating the Vonage Application. Vonage will send POST requests to this URL with updates on message delivery status (e.g.,delivered,failed,rejected). This provides much better insight than just knowing the message was submitted.
11. Troubleshooting and Caveats
Common issues and things to watch out for:
- Authentication Errors:
401 Unauthorizedor similar errors duringsendSms: Usually means incorrectVONAGE_APPLICATION_IDor issues with the private key (VONAGE_PRIVATE_KEY_PATHorVONAGE_PRIVATE_KEY_CONTENT). Double-check the.envfile. If using the path, ensure theprivate.keyfile exists at the specified path and is readable by the Node.js process. If using content, ensure it's correctly formatted (including newlines\nif pasting multi-line keys). Verify the Application ID is correct.
- Initialization Errors: Check the console output when starting the server (
node index.js) for errors related to missing environment variables or problems reading/parsing the private key. - "Non-Whitelisted Destination" Error:
- Cause: Sending to a number not on your verified test number list while using a Vonage trial account.
- Solution: Add the recipient number to your test list in the Vonage Dashboard (Settings -> Test numbers) or upgrade your Vonage account by adding billing details.
- Invalid
fromNumber:- Ensure
VONAGE_FROM_NUMBERin.envis a valid Vonage number that you own and have linked to the Vonage Application being used (see Section 4, Step 5).
- Ensure
- Incorrect API Endpoint/Method:
- Ensure you are making a
POSTrequest to/send-sms(notGET). - Check for typos in the URL (
http://localhost:3000/send-sms).
- Ensure you are making a
- Missing
Content-TypeHeader:- When sending JSON data via
curlor other clients, ensure you set theContent-Type: application/jsonheader. Theexpress.json()middleware requires this.
- When sending JSON data via
- Environment Variables Not Loaded:
- Ensure
require('dotenv').config()is called at the very top ofindex.js. - Verify the
.envfile exists in the project root and contains the correct variables.
- Ensure
- Private Key File Issues (if using path):
- Ensure
VONAGE_PRIVATE_KEY_PATHin.envcorrectly points to theprivate.keyfile relative to where you runnode index.js. - Check file permissions – the Node.js process needs read access to the key file.
- Ensure
- Messages API vs. SMS API Confusion:
- This guide uses the Messages API (
vonage.messages.send). If you see code examples usingvonage.sms.send(...)orvonage.message.sendSms(...), those likely refer to the older SMS API, which typically uses API Key/Secret authentication (new Vonage({ apiKey, apiSecret })) instead of Application ID/Private Key. Ensure your code and SDK initialization match the Messages API approach used here.
- This guide uses the Messages API (
12. Deployment and CI/CD
Deploying a Node.js/Express application:
General Steps:
-
Choose a Platform: Heroku, AWS (EC2, Lambda, Fargate, Elastic Beanstalk), Google Cloud (App Engine, Cloud Run, GKE), Azure (App Service, AKS), DigitalOcean App Platform, Vercel, Railway, etc.
-
Environment Variables: Configure your production environment variables (
PORT,VONAGE_APPLICATION_ID,VONAGE_FROM_NUMBER,NODE_ENV=production, and eitherVONAGE_PRIVATE_KEY_PATHorVONAGE_PRIVATE_KEY_CONTENT) using your chosen platform's mechanism (e.g., Heroku Config Vars, AWS Parameter Store/Secrets Manager). Do not commit.envorprivate.keyto your repository. Provide the private key securely, often via theVONAGE_PRIVATE_KEY_CONTENTvariable for PaaS environments. -
Build Step (Optional): If using TypeScript or a build process, run the build command.
-
Install Production Dependencies: Ensure only necessary dependencies are installed (
npm install --productionoryarn install --production). -
Start Script: Modify your
package.jsonstartscript to run the application in production mode:json// package.json "scripts": { "start": "node index.js", "dev": "nodemon index.js" } -
Procfile (Heroku/Similar): Create a
Procfilein the root:textweb: node index.js
Example: Deploying to Heroku
-
Install Heroku CLI.
-
heroku login -
heroku create your-app-name -
Set Config Vars (replace placeholders). It's usually easiest to provide the private key content directly:
bashheroku config:set NODE_ENV=production heroku config:set VONAGE_APPLICATION_ID=YOUR_APP_ID # Paste the entire content of your private.key file, preserving newlines. # Use single quotes around the value if your shell requires it. heroku config:set VONAGE_PRIVATE_KEY_CONTENT='-----BEGIN PRIVATE KEY-----\nYOUR_MULTI_LINE_KEY_CONTENT...\n-----END PRIVATE KEY-----' heroku config:set VONAGE_FROM_NUMBER=YOUR_VONAGE_NUMBER # PORT is set automatically by Heroku(Note: The
index.jscode from Section 2 already handles readingVONAGE_PRIVATE_KEY_CONTENT) -
Commit
Procfileand your code changes (ensure.envandprivate.keyare in.gitignore). -
git push heroku main(or your branch name). -
heroku logs --tailto monitor.
CI/CD (Example using GitHub Actions):
Create .github/workflows/deploy.yml:
# .github/workflows/deploy.yml
# Basic example for deploying to Heroku on push to main branch
name: Deploy to Heroku
on:
push:
branches:
- main
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Node.js
uses: actions/setup-node@v3
with:
node-version: '18'
- name: Deploy to Heroku
uses: akhileshns/heroku-deploy@v3.12.12
with:
heroku_api_key: ${{ secrets.HEROKU_API_KEY }}
heroku_app_name: "your-heroku-app-name"
heroku_email: ${{ secrets.HEROKU_EMAIL }}
env:
HEROKU_API_KEY: ${{ secrets.HEROKU_API_KEY }}
HEROKU_EMAIL: ${{ secrets.HEROKU_EMAIL }}- Secrets: You need to add
HEROKU_API_KEYandHEROKU_EMAILas secrets in your GitHub repository settings (Settings -> Secrets and variables -> Actions). - Vonage Credentials: Your Vonage credentials should still be configured as Config Vars directly in Heroku (as shown in the Heroku deployment steps), not stored in GitHub secrets for the deployment workflow itself unless absolutely necessary and handled with extreme care.