Platform Integration Guide
This document provides comprehensive guidance for integrating external systems with the MyNATCA Platform, including authentication patterns, API proxy configuration, and best practices.
Platform Integration Overview
The MyNATCA Platform (mynatca-platform) serves as the central integration hub for the ecosystem, providing:
- Authentication Services: OAuth 2.0 with Auth0 integration
- API Proxy Gateway: Bearer token forwarding to external APIs
- Session Management: Redis-backed server-side sessions
- Database Operations: Supabase PostgreSQL integration
- Legacy Data Sync: MySQL to Supabase synchronization
Integration Patterns
1. Frontend Application Integration
Frontend applications (like the Hub) integrate with the Platform using a standard pattern:
Authentication Flow
// Frontend: Request authentication status
const response = await fetch('/api/auth/me', {
method: 'GET',
credentials: 'include' // Include session cookies
});
if (response.ok) {
const user = await response.json();
// User is authenticated, proceed with app logic
} else {
// Redirect to login
window.location.href = '/api/auth/login';
}API Requests Through Proxy
// Frontend: Make API requests via Platform proxy
const memberData = await fetch('/api/mynatca/Member/12985', {
method: 'GET',
credentials: 'include' // Platform handles Bearer token forwarding
});
const profile = await memberData.json();2. External API Integration
Proxy Route Configuration
External APIs are integrated through database-configured proxy routes:
-- Supabase: Configure proxy route
INSERT INTO proxy_routes (path, target, path_rewrite)
VALUES (
'/api/mynatca/*',
'https://dev.my.natca.org/api',
'{"^/api/mynatca/": "/"}'::jsonb
);Proxy Middleware Implementation
The Platform automatically handles Bearer token forwarding for external APIs:
// Platform: middleware/proxy.js
const createProxyMiddleware = require('http-proxy-middleware').createProxyMiddleware;
const proxy = createProxyMiddleware({
target: route.target,
changeOrigin: true,
pathRewrite: route.pathRewrite,
// Critical: v3.0 syntax for callbacks
on: {
proxyReq: (proxyReq, req, res) => {
// Forward JWT token (idToken) from session
if (req.session.user?.idToken) {
proxyReq.setHeader('Authorization', `Bearer ${req.session.user.idToken}`);
console.log('🔑 Forwarding Auth0 ID token to API');
}
},
proxyRes: (proxyRes, req, res) => {
// Enable CORS for proxied responses
proxyRes.headers['Access-Control-Allow-Origin'] = req.headers.origin || '*';
proxyRes.headers['Access-Control-Allow-Credentials'] = 'true';
}
}
});3. Discord Bot Integration
Discord bots integrate with the Platform for authentication and member data:
Member Verification Flow
// Discord Bot: Verify member against Platform
const verifyMember = async (discordUserId, authToken) => {
const response = await fetch(`${PLATFORM_URL}/api/members/verify`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${authToken}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
discordUserId,
provider: 'discord'
})
});
return response.json();
};Role Assignment Based on Member Data
// Discord Bot: Assign roles based on Platform data
const assignMemberRoles = async (member, memberData) => {
const roles = [];
// Facility role
if (memberData.facilityCode) {
roles.push(`facility-${memberData.facilityCode.toLowerCase()}`);
}
// Region role
if (memberData.regionCode) {
roles.push(`region-${memberData.regionCode.toLowerCase()}`);
}
// Position-based roles
if (memberData.positions) {
memberData.positions.forEach(position => {
if (position.title.includes('FacRep')) {
roles.push('facility-representative');
}
});
}
// Apply roles to Discord member
await member.roles.add(roles);
};Authentication Integration
Auth0 Configuration
Custom Claims for NATCA Data
Auth0 Rules inject NATCA-specific data into JWT tokens:
// Auth0 Rule: Add NATCA custom claims
function addNATCACustomClaims(user, context, callback) {
const namespace = 'https://my.natca.org/';
const customClaims = {
[`${namespace}natca_id`]: user.user_metadata.natca_id,
[`${namespace}member_number`]: user.user_metadata.member_number,
[`${namespace}facility_code`]: user.user_metadata.facility_code,
[`${namespace}region_code`]: user.user_metadata.region_code
};
context.idToken = Object.assign(context.idToken, customClaims);
context.accessToken = Object.assign(context.accessToken, customClaims);
callback(null, user, context);
}JWT Token Handling
Critical Implementation Note: The Platform uses JWT idToken for API forwarding, not the encrypted accessToken:
// Platform: session management
app.get('/api/auth/callback', async (req, res) => {
try {
const { user, accessToken, idToken } = await auth0.handleCallback(req, res);
// Store both tokens in session
req.session.user = {
...user,
accessToken, // JWE encrypted - for Auth0 Management API
idToken // JWT signed - for external API forwarding
};
res.redirect('/');
} catch (error) {
res.status(500).json({ error: error.message });
}
});Session Management
Redis Configuration
// Platform: Redis session store
const redis = require('redis');
const RedisStore = require('connect-redis')(session);
const redisClient = redis.createClient({
url: process.env.REDIS_URL,
socket: {
connectTimeout: 10000,
commandTimeout: 5000,
}
});
app.use(session({
store: new RedisStore({ client: redisClient }),
secret: process.env.SESSION_SECRET,
name: 'platform.session',
resave: false,
saveUninitialized: false,
rolling: true,
cookie: {
maxAge: 7 * 24 * 60 * 60 * 1000, // 7 days
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'lax'
}
}));Database Integration
Supabase Integration
Connection Setup
// Platform: lib/supabase.js
import { createClient } from '@supabase/supabase-js';
const supabaseUrl = process.env.SUPABASE_URL;
const supabaseKey = process.env.SUPABASE_SERVICE_ROLE_KEY;
export const supabase = createClient(supabaseUrl, supabaseKey);Row Level Security (RLS)
-- Supabase: Member data access policy
CREATE POLICY "Users can view their own data" ON members
FOR SELECT
USING (auth.jwt() ->> 'sub' = auth_user_id);
CREATE POLICY "Service role has full access" ON members
FOR ALL
USING (auth.role() = 'service_role');Legacy MySQL Synchronization
Sync Configuration
// Platform: sync/config.js
export const syncConfig = {
mysql: {
host: process.env.MYSQL_HOST,
user: process.env.MYSQL_USER,
password: process.env.MYSQL_PASSWORD,
database: process.env.MYSQL_DATABASE,
port: process.env.MYSQL_PORT || 3306
},
supabase: {
url: process.env.SUPABASE_URL,
serviceRoleKey: process.env.SUPABASE_SERVICE_ROLE_KEY
},
batchSize: 1000,
retryAttempts: 3,
retryDelay: 5000
};API Response Patterns
Standardized Response Format
All Platform APIs return a consistent response format:
// TypeScript: API Response interface
interface APIResponse<T> {
success: boolean;
data?: T;
error?: {
code: string;
message: string;
details?: any;
};
meta?: {
timestamp: string;
requestId: string;
version: string;
};
}
// Example successful response
{
"success": true,
"data": {
"id": 12985,
"firstname": "John",
"lastname": "Doe",
"memberNumber": "446754"
},
"meta": {
"timestamp": "2025-01-01T00:00:00Z",
"requestId": "req-123456",
"version": "1.0.0"
}
}
// Example error response
{
"success": false,
"error": {
"code": "MEMBER_NOT_FOUND",
"message": "Member with ID 12985 not found",
"details": {
"searchId": "12985",
"searchType": "natca_id"
}
},
"meta": {
"timestamp": "2025-01-01T00:00:00Z",
"requestId": "req-123456",
"version": "1.0.0"
}
}MyNATCA API Integration
The Platform proxies MyNATCA API requests with automatic response transformation:
// Platform: services/mynatca.js
export interface MyNATCAMemberProfile {
id: number;
firstname: string;
lastname: string;
membernumber: string;
emailAddresses: MemberEmail[];
phones: MemberPhone[];
addresses: MemberAddress[];
status: string;
facility?: MyNATCAFacility;
region?: MyNATCARegion;
}
// Response transformation
export const transformMyNATCAResponse = (rawResponse) => {
if (rawResponse.status === "Success") {
return {
success: true,
data: rawResponse.data,
meta: {
timestamp: new Date().toISOString(),
source: 'mynatca-api',
version: '1.0.0'
}
};
}
return {
success: false,
error: {
code: 'MYNATCA_API_ERROR',
message: rawResponse.message || 'API request failed'
},
meta: {
timestamp: new Date().toISOString(),
source: 'mynatca-api',
version: '1.0.0'
}
};
};Deployment Integration
DigitalOcean App Platform
Multi-Service Configuration
# .do/app.yaml
name: mynatca-ecosystem
services:
# Platform Service
- name: platform
source_dir: /platform
environment_slug: node-js
instance_count: 2
instance_size_slug: professional-xs
http_port: 1300
# Health check configuration
health_check:
http_path: /api/health
initial_delay_seconds: 60
period_seconds: 10
success_threshold: 1
failure_threshold: 3
timeout_seconds: 5
# Environment variables
envs:
- key: NODE_ENV
value: production
- key: SESSION_SECRET
value: ${session.secret}
type: SECRET
- key: REDIS_URL
value: ${redis.url}
type: SECRET
# Hub Service
- name: hub
source_dir: /hub
environment_slug: node-js
instance_count: 2
instance_size_slug: professional-xs
http_port: 1301
routes:
- path: /hub
preserve_path_prefix: true
# Ingress routing
ingress:
rules:
- match:
path:
prefix: /api
component:
name: platform
- match:
path:
prefix: /hub
component:
name: hub
- match:
path:
prefix: /
component:
name: platformEnvironment Variables Management
Development Environment
# .env.development
NODE_ENV=development
SUPABASE_URL=https://your-project.supabase.co
REDIS_URL=redis://localhost:6379/0
VITE_PLATFORM_URL=http://localhost:1300/apiProduction Environment
# DigitalOcean App Platform Environment
NODE_ENV=production
SUPABASE_URL=${supabase.url}
REDIS_URL=${redis.connection_string}
VITE_PLATFORM_URL=https://your-app.ondigitalocean.app/apiSecurity Considerations
API Security
Rate Limiting
// Platform: Rate limiting middleware
const rateLimit = require('express-rate-limit');
const apiLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // Limit each IP to 100 requests per windowMs
message: 'Too many API requests from this IP'
});
app.use('/api/', apiLimiter);Input Validation
// Platform: Request validation
const joi = require('joi');
const validateMemberRequest = (req, res, next) => {
const schema = joi.object({
natcaId: joi.number().integer().positive().required(),
includeContacts: joi.boolean().default(false)
});
const { error, value } = schema.validate(req.body);
if (error) {
return res.status(400).json({
success: false,
error: {
code: 'VALIDATION_ERROR',
message: error.details[0].message
}
});
}
req.validatedData = value;
next();
};CORS Configuration
// Platform: CORS setup
const cors = require('cors');
const corsOptions = {
origin: function (origin, callback) {
const allowedOrigins = [
'http://localhost:1301',
'https://your-hub.ondigitalocean.app'
];
if (!origin || allowedOrigins.includes(origin)) {
callback(null, true);
} else {
callback(new Error('Not allowed by CORS'));
}
},
credentials: true, // Allow cookies
optionsSuccessStatus: 200
};
app.use(cors(corsOptions));Best Practices
Error Handling
- Always return consistent error response format
- Log errors with request context and user information
- Implement retry logic for external API calls
- Use proper HTTP status codes
Performance Optimization
- Implement caching for frequently accessed data
- Use database connection pooling
- Optimize SQL queries with proper indexing
- Implement request/response compression
Monitoring and Logging
- Log all authentication events
- Monitor API response times and error rates
- Track proxy middleware performance
- Implement health checks for all dependencies
Development Workflow
- Use environment-specific configurations
- Implement comprehensive testing for integrations
- Document all integration points and dependencies
- Version API endpoints for backward compatibility
This integration guide provides the foundation for building robust integrations with the MyNATCA Platform while maintaining security, performance, and reliability standards.