Node.js & Express Guide: Vonage Messages API for SMS Sending, Receiving & Delivery Status
This guide provides a step-by-step walkthrough for building a robust Node.js application using the Express framework to send SMS messages, receive inbound SMS, and handle delivery status callbacks via the Vonage Messages API.
We will build an application capable of:
- Sending SMS messages programmatically: Trigger SMS messages to specified recipients via an API endpoint using the Vonage Messages API.
- Receiving inbound SMS messages: Handle messages sent to your Vonage virtual number using Messages API webhooks.
- Tracking SMS delivery status: Receive real-time updates on the delivery status of sent messages (e.g., delivered, failed) via Messages API webhooks.
This solves the common need for applications to reliably communicate with users via SMS and track whether those communications were successfully delivered. Note: While the code snippets illustrate the concepts, they should be rigorously tested in your environment, especially error handling and security implementations.
Technologies Used
- Node.js: A JavaScript runtime environment for server-side development.
- Express: A minimal and flexible Node.js web application framework.
- Vonage Messages API: A unified API for sending and receiving messages across various channels, including SMS. We use this for its comprehensive features, JWT-based webhook security, and webhook capabilities for inbound messages and status updates.
@vonage/server-sdk
: The official Vonage Node.js SDK for interacting with Vonage APIs.dotenv
: A module to load environment variables from a.env
file.jsonwebtoken
: (Conceptually needed for JWT verification, though implementation details are deferred to Vonage docs).- ngrok: A tool to expose local development servers to the internet for testing webhooks during development.
System Architecture
The system involves several components interacting:
- Your Application (Node.js/Express): Hosts the API endpoint for sending SMS and the webhook endpoints for receiving inbound messages and status updates.
- Vonage Platform: Provides the Messages API, virtual numbers, and handles the routing of SMS messages and webhook events.
- User's Mobile Device: Sends and receives SMS messages.
- ngrok (Development): Tunnels requests from a public URL to your local development server. For production, a stable public IP/domain with SSL is required (see Deployment section).
Prerequisites
Before starting, ensure you have the following:
- Vonage API Account: Sign up for free at Vonage API Dashboard. You'll get free credit to start.
- Vonage API Key and Secret: Find these at the top of your Vonage API Dashboard. (Needed for SDK setup, though Application ID/Private Key is primary auth for Messages API calls).
- Vonage Virtual Number: Purchase an SMS-capable number from the dashboard under ""Numbers"" > ""Buy Numbers"".
- Node.js and npm (or yarn): Installed on your system. Download from nodejs.org.
- ngrok: Installed and authenticated. Download from ngrok.com. A free account is sufficient for development. Note that free ngrok URLs are temporary. Paid tiers offer stable domains.
- Basic understanding of Node.js, Express, and APIs.
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-messages-guide cd vonage-messages-guide
-
Initialize Node.js Project: This creates a
package.json
file to manage dependencies and project metadata.npm init -y
(Use
yarn init -y
if you prefer Yarn) -
Install Dependencies: We need
express
for the web server,@vonage/server-sdk
to interact with the Vonage API, anddotenv
to manage environment variables. We'll also installjsonwebtoken
conceptually for webhook security, although the detailed implementation is deferred.npm install express @vonage/server-sdk dotenv jsonwebtoken
(Use
yarn add express @vonage/server-sdk dotenv jsonwebtoken
for Yarn) -
Create Project Structure: Create the necessary files and folders.
touch server.js .env .gitignore vonageClient.js mkdir logs # Optional: For file logging
server.js
: Main application file for the Express server and route handlers..env
: Stores sensitive credentials and configuration (API keys, etc.). Never commit this file to version control..gitignore
: Specifies intentionally untracked files that Git should ignore (like.env
andnode_modules
).vonageClient.js
: Module to initialize and export the Vonage SDK client.logs/
: Directory for log files if using file transport for logging.
-
Configure
.gitignore
: Add the following lines to your.gitignore
file to prevent sensitive information and unnecessary files from being committed:# Dependencies node_modules # Environment variables .env # Logs logs *.log npm-debug.log* yarn-debug.log* yarn-error.log* # Optional Editor directories .idea .vscode *.suo *.ntvs* *.njsproj *.sln # Private Key file private.key *.pem
-
Set up Environment Variables (
.env
): Open the.env
file and add the following variables. We'll fill in the values in the next steps.# Vonage Credentials VONAGE_API_KEY=YOUR_VONAGE_API_KEY VONAGE_API_SECRET=YOUR_VONAGE_API_SECRET VONAGE_APPLICATION_ID=YOUR_VONAGE_APPLICATION_ID VONAGE_PRIVATE_KEY_PATH=./private.key # Path relative to project root VONAGE_NUMBER=YOUR_VONAGE_VIRTUAL_NUMBER # Your purchased Vonage number # Application Config PORT=3000 # Port your local server will run on BASE_WEBHOOK_URL=YOUR_NGROK_FORWARDING_URL # We'll get this from ngrok later
VONAGE_API_KEY
,VONAGE_API_SECRET
: Found on your Vonage dashboard. Used by the SDK, though Application ID/Private Key is primary for Messages API.VONAGE_APPLICATION_ID
,VONAGE_PRIVATE_KEY_PATH
: Obtained when creating a Vonage Application (next section).VONAGE_NUMBER
: Your purchased Vonage virtual phone number in E.164 format (e.g.,14155552671
).PORT
: The local port your Express application will listen on.BASE_WEBHOOK_URL
: The public URL provided by ngrok (or your production server URL).
-
Basic Express Server (
server.js
): Set up a minimal Express server to ensure everything is working.// server.js require('dotenv').config(); // Load .env variables into process.env const express = require('express'); const app = express(); // Middleware to parse JSON bodies // Vonage Messages API webhooks typically use JSON app.use(express.json()); // Include urlencoded parser for flexibility, though JSON is primary for Messages API app.use(express.urlencoded({ extended: true })); const PORT = process.env.PORT || 3000; // Simple root route for testing app.get('/', (req, res) => { res.send('Vonage Messages API Guide App is running!'); }); // Start the server app.listen(PORT, () => { console.log(`Server listening at http://localhost:${PORT}`); });
You can run this with
node server.js
. You should see the confirmation message in your console. Accessinghttp://localhost:3000
in your browser should show the test message.
2. Vonage Account and Application Setup
Now, let's configure your Vonage account and create a Vonage Application to handle Messages API interactions.
-
Retrieve API Key and Secret:
- Log in to your Vonage API Dashboard.
- Your API key and API secret are displayed prominently at the top.
- Copy these values into your
.env
file forVONAGE_API_KEY
andVONAGE_API_SECRET
.
-
Purchase a Vonage Number:
- Navigate to Numbers > Buy numbers.
- Search for a number with SMS capabilities in your desired country.
- Purchase the number.
- Copy the purchased number (in E.164 format, e.g.,
12015550123
) into your.env
file forVONAGE_NUMBER
.
-
Set Default SMS API to ""Messages API"":
- This is crucial for consistency. Navigate to Settings in the dashboard.
- Scroll down to SMS settings.
- Ensure that Default SMS Setting is set to Messages API. If it's set to ""SMS API"", toggle it to ""Messages API"".
- Click Save changes.
- Why? While linking a number to a Messages-enabled Application should force Messages API usage for that app, this account-wide setting ensures consistent Messages API behavior (and webhook formats) for numbers not linked to this specific application or if other authentication methods are ever used inadvertently within your account. It aligns your account's default behavior with this guide's focus.
-
Create a Vonage Application: Vonage Applications act as containers for configuration, security credentials (like the private key), and associated numbers, specifically for APIs like Messages.
- Navigate to Applications > Create a new application.
- Give your application a descriptive Name (e.g., ""Node Messages Guide App"").
- Click Generate public and private key. This will automatically download the
private.key
file. Save this file securely in the root of your project directory (matchingVONAGE_PRIVATE_KEY_PATH
in your.env
). Crucially, addprivate.key
or*.pem
to your.gitignore
file. Vonage stores the public key; you keep the private key to authenticate SDK requests using JWT. - Enable the Messages capability by toggling it on.
- You will see fields for Inbound URL and Status URL. These are the webhook endpoints Vonage will call. For now, enter placeholders using your base URL variable (we'll update these with the real ngrok URL later):
- Inbound URL:
${BASE_WEBHOOK_URL}/webhooks/inbound
(Method:POST
) - Status URL:
${BASE_WEBHOOK_URL}/webhooks/status
(Method:POST
)
- Inbound URL:
- Scroll down to Link virtual numbers and link the Vonage number you purchased earlier to this application. Select the number and click Link.
- Click Generate new application.
- You will be redirected to the application's details page. Copy the Application ID.
- Paste the Application ID into your
.env
file forVONAGE_APPLICATION_ID
.
3. Setting up Webhooks with ngrok (Development)
Webhooks allow Vonage to send real-time data (inbound messages, status updates) to your application. Since your application is running locally during development, you need ngrok to create a secure tunnel from a public URL to your localhost
.
Important: ngrok is excellent for development and testing, but not suitable for production. Production environments require a stable, publicly accessible IP address or domain name with a valid SSL/TLS certificate. See the Deployment section (Section 10) for more details.
-
Run ngrok: Open a new terminal window (keep your Node.js server running or be ready to restart it). Run ngrok, telling it to forward traffic to the port your Express app is listening on (defined in
.env
, default is 3000).ngrok http 3000
-
Get the Forwarding URL: ngrok will display session information, including a public
Forwarding
URL (usually ending in.ngrok.io
or.ngrok-free.app
). It will look something likehttps://<random-string>.ngrok-free.app
. Always use thehttps
version.Session Status online Account Your Name (Plan: Free/Paid) Version x.x.x Region United States (us-cal-1) Web Interface http://127.0.0.1:4040 Forwarding https://<random-string>.ngrok-free.app -> http://localhost:3000 Connections ttl opn rt1 rt5 p50 p90 0 0 0.00 0.00 0.00 0.00
Copy this
https://...
Forwarding URL. Note that free accounts generate a new random URL each time ngrok is started. Paid ngrok plans offer stable subdomains. -
Update
.env
: Paste the copied ngrok Forwarding URL into your.env
file for theBASE_WEBHOOK_URL
variable.# .env (partial) BASE_WEBHOOK_URL=https://<random-string>.ngrok-free.app
-
Update Vonage Application Webhooks:
- Go back to your Vonage Application settings in the dashboard (Applications > click your application name).
- Click Edit.
- Replace the placeholder URLs with your actual ngrok URLs:
- Inbound URL:
https://<random-string>.ngrok-free.app/webhooks/inbound
- Status URL:
https://<random-string>.ngrok-free.app/webhooks/status
- Inbound URL:
- Click Save changes.
Why? Now, when Vonage needs to send an inbound message or a status update related to this application, it will send an HTTP POST request to these public ngrok URLs, which ngrok will forward to your locally running Express application's
/webhooks/inbound
and/webhooks/status
routes. -
Restart Your Node.js Server: If your server is running, stop it (
Ctrl+C
) and restart it (node server.js
) to ensure it picks up the updated.env
configuration (ifBASE_WEBHOOK_URL
is used directly in code, though it's primarily set in the Vonage dashboard).
4. Implementing SMS Sending
Let's create the Vonage SDK client and add an endpoint to send SMS messages using the Messages API.
-
Configure Vonage SDK Client (
vonageClient.js
): This module initializes the Vonage client using credentials from the.env
file. Using Application ID and Private Key is the recommended authentication method for the Messages API as it uses JWTs.// vonageClient.js require('dotenv').config(); const { Vonage } = require('@vonage/server-sdk'); const { readFileSync } = require('fs'); // To read the private key file // Read the private key from the file path specified in .env let privateKeyValue; try { privateKeyValue = readFileSync(process.env.VONAGE_PRIVATE_KEY_PATH); } catch (error) { console.error(`Error reading private key from path: ${process.env.VONAGE_PRIVATE_KEY_PATH}`, error); // Handle error appropriately - maybe exit or use a fallback if applicable // For this guide, we'll proceed, but SDK initialization will likely fail. } const vonage = new Vonage({ apiKey: process.env.VONAGE_API_KEY, // Still required by SDK constructor apiSecret: process.env.VONAGE_API_SECRET, // Still required by SDK constructor applicationId: process.env.VONAGE_APPLICATION_ID, privateKey: privateKeyValue // Use the key content read from the file }, { // Optional: Set custom options here // apiHost: 'https://messages-sandbox.nexmo.com' // Example: Use the sandbox }); module.exports = vonage;
Why use Application ID/Private Key? This method uses JWTs generated by the SDK for authenticating API requests, which is more secure than sending API Key/Secret with each request for server-to-server communication via the Messages API.
-
Create Send SMS Endpoint (
server.js
): Add a new route to yourserver.js
file to handle sending SMS messages. We'll make it aPOST
request that accepts the recipient number and message text in the request body.// server.js (add these parts) const vonage = require('./vonageClient'); // Import the initialized client // Keep existing setup: require('dotenv').config(), express, app, middleware... app.get('/', (req, res) => { // Keep existing root route res.send('Vonage Messages API Guide App is running!'); }); // --- New Route for Sending SMS --- app.post('/send-sms', async (req, res) => { // Basic input validation const { to, text } = req.body; if (!to || !text) { return res.status(400).json({ error: 'Missing ""to"" or ""text"" in request body.' }); } // Consider adding more robust validation (e.g., phone number format using a library) in production const fromNumber = process.env.VONAGE_NUMBER; if (!fromNumber) { console.error('VONAGE_NUMBER is not set in .env'); return res.status(500).json({ error: 'Server configuration error: Missing Vonage number.' }); } console.log(`Attempting to send SMS via Messages API from ${fromNumber} to ${to}`); try { // Use the vonage.messages.send() method for the Messages API const resp = await vonage.messages.send({ channel: 'sms', message_type: 'text', to: to, // Recipient phone number (E.164 format recommended) from: fromNumber, // Your Vonage virtual number linked to the Application text: text // The message content }); console.log('Message submission accepted by Vonage:', resp); // The resp object contains the message_uuid res.status(200).json({ success: true, message_uuid: resp.message_uuid }); } catch (err) { console.error('Error sending SMS via Messages API:', err.response ? JSON.stringify(err.response.data, null, 2) : err.message); // Provide more context if available from the Vonage error response const errorMessage = err.response?.data?.title || err.message || 'Failed to send SMS'; const errorDetail = err.response?.data?.detail || JSON.stringify(err.response?.data); // Include full detail if possible res.status(err.response?.status || 500).json({ success: false, error: errorMessage, detail: errorDetail }); } }); // --- End of New Route --- // ... (Add webhook routes later) // Start the server (keep existing) const PORT = process.env.PORT || 3000; app.listen(PORT, () => { console.log(`Server listening at http://localhost:${PORT}`); });
-
Test Sending SMS: Make sure your Node.js server and ngrok are running. Use
curl
or a tool like Postman/Insomnia to send a POST request to your/send-sms
endpoint. Replace<your-ngrok-url>
with your actual ngrok Forwarding URL and<recipient-phone-number>
with a real phone number (in E.164 format, e.g.,14155552671
).curl -X POST <your-ngrok-url>/send-sms \ -H ""Content-Type: application/json"" \ -d '{ ""to"": ""<recipient-phone-number>"", ""text"": ""Hello from Vonage Messages API and Node.js!"" }'
You should receive an SMS on the recipient phone shortly. Your server console will log the success response from Vonage, including the
message_uuid
, which is crucial for tracking. The API call should return a JSON response like{""success"":true,""message_uuid"":""<some-uuid>""}
. If errors occur, check the console logs for details from thecatch
block.
5. Implementing Inbound SMS Webhook
This webhook handles SMS messages sent to your Vonage virtual number, delivered via the Messages API infrastructure because you linked the number to your Application and set the defaults appropriately.
-
Create Inbound Webhook Route (
server.js
): Add aPOST
route handler for the/webhooks/inbound
path you configured in your Vonage Application.// server.js (add this route) // ... (existing code: imports, middleware, /send-sms route) // --- New Route for Inbound SMS --- app.post('/webhooks/inbound', (req, res) => { console.log('--- Inbound SMS Webhook Received ---'); console.log('Request Body:', JSON.stringify(req.body, null, 2)); // Log headers for debugging signature verification later console.log('Request Headers:', JSON.stringify(req.headers, null, 2)); // --- Security Check Placeholder --- // IMPORTANT: Implement JWT verification here for production! // See Section 8 (Security Considerations) and Vonage documentation. // verifyVonageSignature(req); // This function needs proper implementation // Process the inbound message (assuming signature verification passed or is deferred) const message = req.body; // Check structure for typical Messages API inbound SMS if (message.from?.type === 'sms' && message.message?.content?.type === 'text') { console.log(`From: ${message.from.number}`); console.log(`To: ${message.to.number}`); // Your Vonage number console.log(`Text: ${message.message.content.text}`); console.log(`Message UUID: ${message.message_uuid}`); console.log(`Timestamp: ${message.timestamp}`); // --- Add your business logic here --- // Example: Store the message, trigger a reply, etc. // e.g., replyToSms(message.from.number, 'Thanks for your message!'); } else { console.warn('Received webhook does not appear to be a standard inbound SMS via Messages API.'); } // Vonage needs a 200 OK response quickly to acknowledge receipt. // Do complex processing asynchronously if needed. res.status(200).end(); }); // --- End of Inbound Route --- // ... (Add status webhook route next) // Start the server (keep existing) // ...
-
Understanding Webhook Signature Verification (JWT): The Messages API secures webhooks using JWT (JSON Web Tokens) attached to the
Authorization
header (typicallyBearer <token>
). Verifying this signature is critical for production security to ensure requests genuinely come from Vonage.- How it Works (Concept): Vonage signs the JWT using the private key associated with your Vonage Application. Your application needs the corresponding public key to verify the signature.
- Obtaining the Public Key: This is often the trickiest part. The public key isn't usually downloaded like the private key. You might need to:
- Check if the Vonage SDK provides a helper function to fetch or manage keys.
- Look for API endpoints or dashboard sections that expose the public key associated with your Application ID.
- Potentially derive the public key from your private key using cryptographic libraries (ensure compatibility with Vonage's format).
- Verification Libraries: Use standard JWT libraries like
jsonwebtoken
(which we installed) to perform the verification against the public key. - Implementation: Implementing this correctly involves fetching the right key, handling potential key rotation, and using the library properly. Refer to the official Vonage Developer Documentation on "Signed Webhooks" or "Webhook Security" specifically for the Messages API for the authoritative and up-to-date implementation guide. The exact mechanism can evolve.
This guide does not provide a ready-to-use JWT verification snippet due to the complexity and variability in obtaining the correct public key. You must consult Vonage documentation and implement this verification before deploying to production.
-
Test Inbound SMS:
- Ensure your Node.js server and ngrok are running.
- Using your mobile phone, send an SMS message to your Vonage virtual number (
VONAGE_NUMBER
). - Watch your server console. You should see the "--- Inbound SMS Webhook Received ---" log message, followed by the parsed request body and headers. Verify the message content and sender number are logged correctly.
- Check the ngrok web interface (
http://127.0.0.1:4040
) to inspect the incoming POST request to/webhooks/inbound
, including theAuthorization
header containing the JWT.
6. Implementing Status Callback Webhook
This webhook receives updates about the delivery status of messages you sent using the Messages API via this Vonage Application.
-
Create Status Webhook Route (
server.js
): Add aPOST
route handler for the/webhooks/status
path.// server.js (add this route) // ... (existing code: imports, middleware, /send-sms, /webhooks/inbound) // --- New Route for Delivery Status Updates --- app.post('/webhooks/status', (req, res) => { console.log('--- Delivery Status Webhook Received ---'); console.log('Request Body:', JSON.stringify(req.body, null, 2)); // Log headers for debugging signature verification later console.log('Request Headers:', JSON.stringify(req.headers, null, 2)); // --- Security Check Placeholder --- // IMPORTANT: Implement JWT verification here for production! // Use the same logic/mechanism as for the inbound webhook. // See Section 8 (Security Considerations) and Vonage documentation. // verifyVonageSignature(req); // This function needs proper implementation // Process the status update (assuming signature verification passed or is deferred) const statusUpdate = req.body; // Key fields in a Messages API status update typically include: // message_uuid: The ID of the message this status refers to. // status: e.g., ""submitted"", ""delivered"", ""rejected"", ""undeliverable"", ""failed"" // timestamp: When the status event occurred. // to: { type: 'sms', number: 'RECIPIENT_NUMBER' } // from: { type: 'sms', number: 'YOUR_VONAGE_NUMBER' } // error: { code: ..., reason: ... } (present on failure/rejection) // usage: { currency: ..., price: ... } (sometimes included) if (statusUpdate.message_uuid && statusUpdate.status) { console.log(`Status for message ${statusUpdate.message_uuid}: ${statusUpdate.status}`); console.log(`Timestamp: ${statusUpdate.timestamp}`); console.log(`Recipient: ${statusUpdate.to?.number}`); // Added recipient logging if (statusUpdate.error) { console.error(`Error Code: ${statusUpdate.error.code}`); console.error(`Error Reason: ${statusUpdate.error.reason}`); } if (statusUpdate.usage) { console.log(`Usage: Price ${statusUpdate.usage.price} ${statusUpdate.usage.currency}`); } // --- Add your business logic here --- // Example: Update the status of the message in your database using message_uuid // updateMessageStatusInDB(statusUpdate.message_uuid, statusUpdate.status, statusUpdate.error, statusUpdate.timestamp); } else { console.warn('Received webhook does not appear to be a valid Messages API status update.'); } // Vonage needs a 200 OK response. res.status(200).end(); }); // --- End of Status Route --- // Start the server (keep existing) // ...
-
Test Status Updates:
- Ensure your server and ngrok are running.
- Send an SMS using the
/send-sms
endpoint (Step 4). Note themessage_uuid
returned. - Watch your server console. You should receive one or more status updates for that
message_uuid
via the/webhooks/status
endpoint. Common statuses include:submitted
: Vonage has accepted the message request.delivered
: The recipient's carrier confirmed delivery to the handset.rejected
: Vonage rejected the message before sending (e.g., invalid number format before carrier, insufficient funds). Check theerror
field.undeliverable
/failed
: The carrier could not deliver the message (e.g., phone off for extended period, invalid number on carrier network, blocked by user/carrier). Check theerror
field.
- Check the ngrok web interface (
http://127.0.0.1:4040
) to inspect the incoming POST requests to/webhooks/status
, including the JWT in theAuthorization
header.
7. Error Handling and Logging
Robust error handling and clear logging are essential for debugging and maintaining the application.
-
Error Handling:
- API Calls: We wrapped the
vonage.messages.send
call in atry...catch
block. Log detailed error information fromerr.response.data
if available, as it contains specific Vonage error details. Return appropriate HTTP status codes (e.g., 400 for bad input, 500 for server errors, Vonage status codes if meaningful). - Webhooks: Ensure webhook handlers always return a
200 OK
status to Vonage quickly, even if internal processing fails. Log errors encountered during webhook processing for later debugging. Usetry...catch
within the webhook handlers for your business logic to prevent crashes from stopping theres.status(200).end()
call. Handle asynchronous operations carefully. - Configuration: Check for the existence and validity of necessary environment variables (like
VONAGE_NUMBER
,VONAGE_APPLICATION_ID
,VONAGE_PRIVATE_KEY_PATH
) at startup or before use. ThevonageClient.js
example includes a basic check for the private key file.
- API Calls: We wrapped the
-
Logging:
- Use
console.log
,console.warn
, andconsole.error
consistently for basic logging. - Log key events: server start, incoming API requests, outgoing API calls, successful operations, errors (with stack traces or detailed Vonage responses), and full webhook payloads/headers (especially during development).
- Include unique identifiers like
message_uuid
in logs to trace the lifecycle of a message across sending, status updates, and potential inbound replies. - For production: Use a more structured logging library like Winston or Pino. These libraries offer:
- Different log levels (debug, info, warn, error, critical).
- Structured formatting (JSON is common for log aggregation systems).
- Multiple transports (writing to console, files, databases, external logging services like Datadog, Loggly, etc.).
// Example using Winston (requires npm install winston) const winston = require('winston'); const path = require('path'); const logger = winston.createLogger({ level: process.env.LOG_LEVEL || 'info', // Control log level via env var format: winston.format.combine( winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }), winston.format.errors({ stack: true }), // Log stack traces winston.format.splat(), winston.format.json() // Log as JSON ), defaultMeta: { service: 'vonage-sms-app' }, // Add service name to logs transports: [ // Write errors to error.log new winston.transports.File({ filename: path.join('logs', 'error.log'), level: 'error' }), // Write all logs (info and above) to combined.log new winston.transports.File({ filename: path.join('logs', 'combined.log') }) ], }); // If not in production, also log to the console with a simpler format if (process.env.NODE_ENV !== 'production') { logger.add(new winston.transports.Console({ format: winston.format.combine( winston.format.colorize(), winston.format.simple() ) })); } // Usage example (replace console.log/error): // logger.info('Server started on port %d', PORT); // logger.error('Failed to send SMS:', err); // logger.warn('Webhook received with unexpected format:', req.body); // logger.debug('Detailed webhook body:', req.body); // Only logs if level is 'debug'
- Use