first commit
This commit is contained in:
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
.node_modules/
|
||||||
|
.env
|
||||||
|
.idea/
|
||||||
1010
package-lock.json
generated
Normal file
1010
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
18
package.json
Normal file
18
package.json
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"name": "postgres-api",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"cors": "^2.8.5",
|
||||||
|
"dotenv": "^17.2.3",
|
||||||
|
"express": "^5.2.1",
|
||||||
|
"pg": "^8.17.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
266
server.js
Normal file
266
server.js
Normal file
@@ -0,0 +1,266 @@
|
|||||||
|
const express = require('express');
|
||||||
|
const { Pool } = require('pg');
|
||||||
|
const dotenv = require('dotenv');
|
||||||
|
const cors = require('cors');
|
||||||
|
|
||||||
|
// Load environment variables
|
||||||
|
dotenv.config();
|
||||||
|
|
||||||
|
const app = express();
|
||||||
|
const PORT = process.env.PORT || 3000;
|
||||||
|
|
||||||
|
// Middleware
|
||||||
|
app.use(cors());
|
||||||
|
app.use(express.json());
|
||||||
|
|
||||||
|
// PostgreSQL connection pool
|
||||||
|
const pool = new Pool({
|
||||||
|
host: process.env.DB_HOST,
|
||||||
|
port: process.env.DB_PORT,
|
||||||
|
database: process.env.DB_DATABASE,
|
||||||
|
user: process.env.DB_USER,
|
||||||
|
password: process.env.DB_PASSWORD,
|
||||||
|
max: 20,
|
||||||
|
idleTimeoutMillis: 30000,
|
||||||
|
connectionTimeoutMillis: 2000,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Health check endpoint
|
||||||
|
app.get('/api/health', (req, res) => {
|
||||||
|
res.json({
|
||||||
|
status: 'ok',
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
node_version: process.version
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// SELECT query endpoint
|
||||||
|
app.post('/api/postgres/select', async (req, res) => {
|
||||||
|
const { query, bindings = [] } = req.body;
|
||||||
|
|
||||||
|
if (!query) {
|
||||||
|
return res.status(422).json({
|
||||||
|
success: false,
|
||||||
|
error: 'Query is required'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await pool.query(query, bindings);
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
success: true,
|
||||||
|
data: result.rows,
|
||||||
|
count: result.rowCount
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('SELECT Error:', error);
|
||||||
|
res.status(500).json({
|
||||||
|
success: false,
|
||||||
|
error: error.message
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// EXECUTE query endpoint (INSERT, UPDATE, DELETE)
|
||||||
|
app.post('/api/postgres/execute', async (req, res) => {
|
||||||
|
const { query, bindings = [] } = req.body;
|
||||||
|
|
||||||
|
if (!query) {
|
||||||
|
return res.status(422).json({
|
||||||
|
success: false,
|
||||||
|
error: 'Query is required'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await pool.query(query, bindings);
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
success: true,
|
||||||
|
affected_rows: result.rowCount
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('EXECUTE Error:', error);
|
||||||
|
res.status(500).json({
|
||||||
|
success: false,
|
||||||
|
error: error.message
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// TRANSACTION endpoint
|
||||||
|
app.post('/api/postgres/transaction', async (req, res) => {
|
||||||
|
const { queries } = req.body;
|
||||||
|
|
||||||
|
if (!queries || !Array.isArray(queries)) {
|
||||||
|
return res.status(422).json({
|
||||||
|
success: false,
|
||||||
|
error: 'Queries array is required'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const client = await pool.connect();
|
||||||
|
|
||||||
|
try {
|
||||||
|
await client.query('BEGIN');
|
||||||
|
|
||||||
|
const results = [];
|
||||||
|
for (const item of queries) {
|
||||||
|
const result = await client.query(item.query, item.bindings || []);
|
||||||
|
results.push({
|
||||||
|
rowCount: result.rowCount,
|
||||||
|
success: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
await client.query('COMMIT');
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
success: true,
|
||||||
|
results: results
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
await client.query('ROLLBACK');
|
||||||
|
console.error('TRANSACTION Error:', error);
|
||||||
|
|
||||||
|
res.status(500).json({
|
||||||
|
success: false,
|
||||||
|
error: error.message
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
client.release();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// INSERT simplified endpoint
|
||||||
|
app.post('/api/postgres/insert', async (req, res) => {
|
||||||
|
const { table, data } = req.body;
|
||||||
|
|
||||||
|
if (!table || !data) {
|
||||||
|
return res.status(422).json({
|
||||||
|
success: false,
|
||||||
|
error: 'Table and data are required'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const columns = Object.keys(data);
|
||||||
|
const values = Object.values(data);
|
||||||
|
const placeholders = values.map((_, i) => `$${i + 1}`).join(', ');
|
||||||
|
|
||||||
|
const query = `
|
||||||
|
INSERT INTO "${table}" (${columns.map(c => `"${c}"`).join(', ')})
|
||||||
|
VALUES (${placeholders})
|
||||||
|
RETURNING *
|
||||||
|
`;
|
||||||
|
|
||||||
|
const result = await pool.query(query, values);
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
success: true,
|
||||||
|
data: result.rows[0]
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('INSERT Error:', error);
|
||||||
|
res.status(500).json({
|
||||||
|
success: false,
|
||||||
|
error: error.message
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// UPDATE simplified endpoint
|
||||||
|
app.post('/api/postgres/update', async (req, res) => {
|
||||||
|
const { table, data, where } = req.body;
|
||||||
|
|
||||||
|
if (!table || !data || !where) {
|
||||||
|
return res.status(422).json({
|
||||||
|
success: false,
|
||||||
|
error: 'Table, data and where are required'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const setColumns = Object.keys(data);
|
||||||
|
const setValues = Object.values(data);
|
||||||
|
const whereColumns = Object.keys(where);
|
||||||
|
const whereValues = Object.values(where);
|
||||||
|
|
||||||
|
const setClause = setColumns
|
||||||
|
.map((col, i) => `"${col}" = $${i + 1}`)
|
||||||
|
.join(', ');
|
||||||
|
|
||||||
|
const whereClause = whereColumns
|
||||||
|
.map((col, i) => `"${col}" = $${setValues.length + i + 1}`)
|
||||||
|
.join(' AND ');
|
||||||
|
|
||||||
|
const query = `
|
||||||
|
UPDATE "${table}"
|
||||||
|
SET ${setClause}
|
||||||
|
WHERE ${whereClause}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const result = await pool.query(query, [...setValues, ...whereValues]);
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
success: true,
|
||||||
|
affected_rows: result.rowCount
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('UPDATE Error:', error);
|
||||||
|
res.status(500).json({
|
||||||
|
success: false,
|
||||||
|
error: error.message
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// DELETE simplified endpoint
|
||||||
|
app.post('/api/postgres/delete', async (req, res) => {
|
||||||
|
const { table, where } = req.body;
|
||||||
|
|
||||||
|
if (!table || !where) {
|
||||||
|
return res.status(422).json({
|
||||||
|
success: false,
|
||||||
|
error: 'Table and where are required'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const whereColumns = Object.keys(where);
|
||||||
|
const whereValues = Object.values(where);
|
||||||
|
|
||||||
|
const whereClause = whereColumns
|
||||||
|
.map((col, i) => `"${col}" = $${i + 1}`)
|
||||||
|
.join(' AND ');
|
||||||
|
|
||||||
|
const query = `DELETE FROM "${table}" WHERE ${whereClause}`;
|
||||||
|
|
||||||
|
const result = await pool.query(query, whereValues);
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
success: true,
|
||||||
|
affected_rows: result.rowCount
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('DELETE Error:', error);
|
||||||
|
res.status(500).json({
|
||||||
|
success: false,
|
||||||
|
error: error.message
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Start server
|
||||||
|
app.listen(PORT, () => {
|
||||||
|
console.log(`✓ Postgres API running on http://localhost:${PORT}`);
|
||||||
|
console.log(`✓ Health check: http://localhost:${PORT}/api/health`);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handle shutdown gracefully
|
||||||
|
process.on('SIGINT', async () => {
|
||||||
|
console.log('\nShutting down gracefully...');
|
||||||
|
await pool.end();
|
||||||
|
process.exit(0);
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user