Skip to content

Nodejs Example

This example demonstrates how to integrate with the Engagifii API using JavaScript.

Code Example

javascript
// Node.js Integration Example for Bill Tracking API
// Complete Express.js application with middleware, error handling, and caching

const express = require('express');
const axios = require('axios');
const NodeCache = require('node-cache');
const rateLimit = require('express-rate-limit');
const helmet = require('helmet');
const morgan = require('morgan');
require('dotenv').config();

// Configuration
const config = {
  apiUrl: process.env.BILLTRACKING_API_URL || 'https://engagifii-billtracking.azurewebsites.net',
  apiVersion: process.env.BILLTRACKING_API_VERSION || '1.0',
  tenantCode: process.env.BILLTRACKING_TENANT_CODE || 'YOUR_TENANT_CODE',
  port: process.env.PORT || 3000,
  cacheTimeout: parseInt(process.env.CACHE_TIMEOUT) || 300 // 5 minutes
};

// Initialize Express app
const app = express();

// Middleware
app.use(helmet());
app.use(morgan('combined'));
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

// Initialize cache
const cache = new NodeCache({ stdTTL: config.cacheTimeout });

// Rate limiting for our API
const limiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 100 // limit each IP to 100 requests per windowMs
});
app.use('/api/', limiter);

// Create axios instance for Bill Tracking API
const billTrackingAPI = axios.create({
  baseURL: config.apiUrl,
  timeout: 30000,
  headers: {
    'api-version': config.apiVersion,
    'tenant-code': config.tenantCode,
    'Content-Type': 'application/json'
  }
});

// Request interceptor for logging
billTrackingAPI.interceptors.request.use(
  request => {
    console.log(`API Request: ${request.method?.toUpperCase()} ${request.url}`);
    return request;
  },
  error => {
    console.error('Request error:', error);
    return Promise.reject(error);
  }
);

// Response interceptor for error handling and retry logic
billTrackingAPI.interceptors.response.use(
  response => {
    console.log(`API Response: ${response.status} ${response.config.url}`);
    return response;
  },
  async error => {
    const originalRequest = error.config;

    // Retry logic for rate limiting
    if (error.response?.status === 429 && !originalRequest._retry) {
      originalRequest._retry = true;
      const retryAfter = error.response.headers['retry-after'] || 60;
      console.warn(`Rate limited. Retrying after ${retryAfter}s`);
      await new Promise(resolve => setTimeout(resolve, retryAfter * 1000));
      return billTrackingAPI(originalRequest);
    }

    // Retry logic for server errors
    if (error.response?.status >= 500 && !originalRequest._retry) {
      originalRequest._retry = true;
      console.warn('Server error. Retrying request...');
      await new Promise(resolve => setTimeout(resolve, 2000));
      return billTrackingAPI(originalRequest);
    }

    return Promise.reject(error);
  }
);

// API Service Class
class BillTrackingService {
  // Get states with caching
  async getStates() {
    const cacheKey = 'states';
    let states = cache.get(cacheKey);
    
    if (!states) {
      const response = await billTrackingAPI.get(`/api/${config.apiVersion}/dropdown/states`);
      states = response.data;
      cache.set(cacheKey, states, 3600); // Cache for 1 hour
    }
    
    return states;
  }

  // Get sessions with optional state filter
  async getSessions(stateId = null) {
    const cacheKey = `sessions_${stateId || 'all'}`;
    let sessions = cache.get(cacheKey);
    
    if (!sessions) {
      const params = stateId ? { stateId } : {};
      const response = await billTrackingAPI.get(`/api/${config.apiVersion}/dropdown/sessions`, { params });
      sessions = response.data;
      cache.set(cacheKey, sessions, 1800); // Cache for 30 minutes
    }
    
    return sessions;
  }

  // Get bill calendar events
  async getCalendarEvents(filters = {}) {
    const defaultFilters = {
      startDate: new Date().toISOString().split('T')[0],
      endDate: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000).toISOString().split('T')[0],
      pageNumber: 1,
      pageSize: 50,
      ...filters
    };

    const response = await billTrackingAPI.post(
      `/api/${config.apiVersion}/bill/event/calendar`,
      defaultFilters
    );
    
    return response.data;
  }

  // Save activity log
  async logActivity(userId, action, entityType, entityId, metadata = {}) {
    const logEntry = {
      userId,
      action,
      entityType,
      entityId,
      metadata,
      timestamp: new Date().toISOString()
    };

    const response = await billTrackingAPI.post(
      `/api/${config.apiVersion}/activity/log/save`,
      logEntry
    );
    
    return response.data;
  }

  // Get activity logs
  async getActivityLogs(filters = {}) {
    const defaultFilters = {
      pageNumber: 1,
      pageSize: 50,
      sortBy: 'timestamp',
      isAscending: false,
      ...filters
    };

    const response = await billTrackingAPI.post(
      `/api/${config.apiVersion}/activity/log/list`,
      defaultFilters
    );
    
    return response.data;
  }

  // Save campaign template
  async saveCampaignTemplate(campaignData) {
    const response = await billTrackingAPI.post(
      `/api/${config.apiVersion}/Advocacy/saveChampaignTemplate`,
      campaignData
    );
    
    return response.data;
  }

  // Get campaign template
  async getCampaignTemplate(id) {
    const response = await billTrackingAPI.get(
      `/api/${config.apiVersion}/Advocacy/getChampaignTemplate/${id}`
    );
    
    return response.data;
  }

  // List campaigns
  async listCampaigns(filters = {}) {
    const defaultFilters = {
      pageNumber: 1,
      pageSize: 20,
      isActive: true,
      ...filters
    };

    const response = await billTrackingAPI.post(
      `/api/${config.apiVersion}/Advocacy/list`,
      defaultFilters
    );
    
    return response.data;
  }

  // Subscribe to bill events
  async subscribeToBillEvents(subscriptions) {
    const response = await billTrackingAPI.post(
      `/api/${config.apiVersion}/bill/event/subscribe`,
      subscriptions
    );
    
    return response.data;
  }

  // Get subscriptions
  async getSubscriptions() {
    const response = await billTrackingAPI.post(
      `/api/${config.apiVersion}/bill/event/Getsubscriptions`
    );
    
    return response.data;
  }
}

// Initialize service
const billTrackingService = new BillTrackingService();

// Express Routes

// Health check
app.get('/health', (req, res) => {
  res.json({ 
    status: 'healthy', 
    timestamp: new Date().toISOString(),
    config: {
      apiUrl: config.apiUrl,
      apiVersion: config.apiVersion,
      cacheStats: cache.getStats()
    }
  });
});

// Get states
app.get('/api/states', async (req, res, next) => {
  try {
    const states = await billTrackingService.getStates();
    res.json({ success: true, data: states });
  } catch (error) {
    next(error);
  }
});

// Get sessions
app.get('/api/sessions', async (req, res, next) => {
  try {
    const { stateId } = req.query;
    const sessions = await billTrackingService.getSessions(stateId);
    res.json({ success: true, data: sessions });
  } catch (error) {
    next(error);
  }
});

// Get calendar events
app.post('/api/calendar/events', async (req, res, next) => {
  try {
    const events = await billTrackingService.getCalendarEvents(req.body);
    res.json({ success: true, data: events });
  } catch (error) {
    next(error);
  }
});

// Log activity
app.post('/api/activity/log', async (req, res, next) => {
  try {
    const { userId, action, entityType, entityId, metadata } = req.body;
    const result = await billTrackingService.logActivity(
      userId, 
      action, 
      entityType, 
      entityId, 
      metadata
    );
    res.json({ success: true, data: result });
  } catch (error) {
    next(error);
  }
});

// Get activity logs
app.post('/api/activity/list', async (req, res, next) => {
  try {
    const logs = await billTrackingService.getActivityLogs(req.body);
    res.json({ success: true, data: logs });
  } catch (error) {
    next(error);
  }
});

// Campaign routes
app.post('/api/campaigns', async (req, res, next) => {
  try {
    const campaignId = await billTrackingService.saveCampaignTemplate(req.body);
    res.json({ success: true, data: { id: campaignId } });
  } catch (error) {
    next(error);
  }
});

app.get('/api/campaigns/:id', async (req, res, next) => {
  try {
    const campaign = await billTrackingService.getCampaignTemplate(req.params.id);
    res.json({ success: true, data: campaign });
  } catch (error) {
    next(error);
  }
});

app.post('/api/campaigns/list', async (req, res, next) => {
  try {
    const campaigns = await billTrackingService.listCampaigns(req.body);
    res.json({ success: true, data: campaigns });
  } catch (error) {
    next(error);
  }
});

// Event subscriptions
app.post('/api/subscriptions', async (req, res, next) => {
  try {
    const result = await billTrackingService.subscribeToBillEvents(req.body);
    res.json({ success: true, data: result });
  } catch (error) {
    next(error);
  }
});

app.get('/api/subscriptions', async (req, res, next) => {
  try {
    const subscriptions = await billTrackingService.getSubscriptions();
    res.json({ success: true, data: subscriptions });
  } catch (error) {
    next(error);
  }
});

// Clear cache endpoint (for admin use)
app.post('/api/cache/clear', (req, res) => {
  cache.flushAll();
  res.json({ success: true, message: 'Cache cleared successfully' });
});

// Error handling middleware
app.use((err, req, res, next) => {
  console.error('Error:', err);
  
  // Handle Axios errors
  if (err.response) {
    return res.status(err.response.status).json({
      success: false,
      error: err.response.data?.error || 'API Error',
      message: err.response.data?.message || err.message,
      details: err.response.data?.details
    });
  }
  
  // Handle other errors
  res.status(500).json({
    success: false,
    error: 'Internal Server Error',
    message: err.message || 'An unexpected error occurred'
  });
});

// 404 handler
app.use((req, res) => {
  res.status(404).json({
    success: false,
    error: 'Not Found',
    message: `Cannot ${req.method} ${req.url}`
  });
});

// Graceful shutdown
process.on('SIGTERM', () => {
  console.log('SIGTERM signal received: closing HTTP server');
  server.close(() => {
    console.log('HTTP server closed');
    process.exit(0);
  });
});

// Start server
const server = app.listen(config.port, () => {
  console.log(`Bill Tracking API Integration Server running on port ${config.port}`);
  console.log(`API URL: ${config.apiUrl}`);
  console.log(`API Version: ${config.apiVersion}`);
});

module.exports = { app, billTrackingService };

Usage Notes

  • Make sure to replace placeholder values with your actual API credentials
  • Install required dependencies before running this code
  • Refer to the main API documentation for detailed endpoint information

Download

Download this example: nodejs-example.js