dataflow/api/server.js
Paul Trowbridge 0ece53e7be Fix pg deprecation warning: set search_path via connection options
Replace pool.on('connect') query with connection-level options parameter.
Avoids calling client.query() during handshake, which pg will remove in v9.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 23:36:27 -04:00

112 lines
3.1 KiB
JavaScript

/**
* Dataflow API Server
* Simple REST API for data transformation
*/
require('dotenv').config({ quiet: true });
const express = require('express');
const { Pool } = require('pg');
const app = express();
const PORT = process.env.API_PORT || 3000;
// Database connection
const pool = new Pool({
host: process.env.DB_HOST,
port: process.env.DB_PORT,
database: process.env.DB_NAME,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
options: '-c search_path=dataflow,public'
});
// Middleware
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
// Auth — protect all /api routes (health and static UI are exempt)
const auth = require('./middleware/auth');
app.use('/api', auth);
// Serve UI
const path = require('path');
app.use(express.static(path.join(__dirname, '../public')));
// Test database connection
pool.query('SELECT NOW()', (err, res) => {
if (err) {
console.error('Database connection error:', err);
process.exit(1);
}
console.log('✓ Database connected');
});
//------------------------------------------------------
// Routes
//------------------------------------------------------
// Import route modules
const sourcesRoutes = require('./routes/sources');
const rulesRoutes = require('./routes/rules');
const mappingsRoutes = require('./routes/mappings');
const recordsRoutes = require('./routes/records');
const stacksRoutes = require('./routes/stacks');
const statusRoutes = require('./routes/status');
// Mount routes
app.use('/api/sources', sourcesRoutes(pool));
app.use('/api/rules', rulesRoutes(pool));
app.use('/api/mappings', mappingsRoutes(pool));
app.use('/api/records', recordsRoutes(pool));
app.use('/api/stacks', stacksRoutes(pool));
app.use('/api/status', statusRoutes(pool));
// Health check
app.get('/health', (req, res) => {
res.json({ status: 'ok', timestamp: new Date().toISOString() });
});
// Root endpoint
app.get('/', (req, res) => {
res.json({
name: 'Dataflow API',
version: '0.1.0',
endpoints: {
sources: '/api/sources',
rules: '/api/rules',
mappings: '/api/mappings',
records: '/api/records',
health: '/health'
}
});
});
// Error handler
app.use((err, req, res, next) => {
console.error('Error:', err);
res.status(err.status || 500).json({
error: err.message || 'Internal server error',
...(process.env.NODE_ENV === 'development' && { stack: err.stack })
});
});
// SPA fallback — serve index.html for any non-API route
app.use((req, res, next) => {
if (req.path.startsWith('/api')) return next();
res.sendFile(path.join(__dirname, '../public/index.html'));
});
// 404 handler (API routes only)
app.use((req, res) => {
res.status(404).json({ error: 'Endpoint not found' });
});
// Start server
app.listen(PORT, '0.0.0.0', () => {
console.log(`✓ Dataflow API listening on port ${PORT}`);
console.log(` Health: http://localhost:${PORT}/health`);
console.log(` API: http://localhost:${PORT}/api/sources`);
});
module.exports = app;