Switch demo to Postgres

This commit is contained in:
“Naeel” 2026-02-22 16:14:53 +04:00
parent 417ee230a4
commit 6e8edaad61
4 changed files with 162 additions and 116 deletions

View File

@ -1,4 +1,4 @@
# Node + MariaDB Demo # NodeJS + Postgres Demo
Simple Node.js CRUD app that writes to `nubes_test_table` and renders a small HTML UI. Simple Node.js CRUD app that writes to `nubes_test_table` and renders a small HTML UI.
If the input is empty, it inserts "Node did it". If the input is empty, it inserts "Node did it".

230
package-lock.json generated
View File

@ -15,7 +15,7 @@
"@loopback/rest-explorer": "^3.3.1", "@loopback/rest-explorer": "^3.3.1",
"@loopback/service-proxy": "^3.2.1", "@loopback/service-proxy": "^3.2.1",
"loopback-connector-rest": "^3.7.0", "loopback-connector-rest": "^3.7.0",
"mysql2": "^3.11.0", "pg": "^8.18.0",
"tslib": "^2.0.0" "tslib": "^2.0.0"
}, },
"devDependencies": { "devDependencies": {
@ -1479,13 +1479,6 @@
"node": "*" "node": "*"
} }
}, },
"node_modules/aws-ssl-profiles": {
"version": "1.1.2",
"license": "MIT",
"engines": {
"node": ">= 6.0.0"
}
},
"node_modules/aws4": { "node_modules/aws4": {
"version": "1.11.0", "version": "1.11.0",
"license": "MIT" "license": "MIT"
@ -2035,13 +2028,6 @@
"node": ">=0.4.0" "node": ">=0.4.0"
} }
}, },
"node_modules/denque": {
"version": "2.1.0",
"license": "Apache-2.0",
"engines": {
"node": ">=0.10"
}
},
"node_modules/depd": { "node_modules/depd": {
"version": "2.0.0", "version": "2.0.0",
"license": "MIT", "license": "MIT",
@ -2964,13 +2950,6 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/generate-function": {
"version": "2.3.1",
"license": "MIT",
"dependencies": {
"is-property": "^1.0.2"
}
},
"node_modules/gensync": { "node_modules/gensync": {
"version": "1.0.0-beta.2", "version": "1.0.0-beta.2",
"dev": true, "dev": true,
@ -3500,10 +3479,6 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/is-property": {
"version": "1.0.2",
"license": "MIT"
},
"node_modules/is-regex": { "node_modules/is-regex": {
"version": "1.1.3", "version": "1.1.3",
"license": "MIT", "license": "MIT",
@ -4037,10 +4012,6 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/long": {
"version": "5.3.2",
"license": "Apache-2.0"
},
"node_modules/loopback-connector": { "node_modules/loopback-connector": {
"version": "5.0.1", "version": "5.0.1",
"license": "MIT", "license": "MIT",
@ -4151,19 +4122,6 @@
"node": ">=10" "node": ">=10"
} }
}, },
"node_modules/lru.min": {
"version": "1.1.4",
"license": "MIT",
"engines": {
"bun": ">=1.0.0",
"deno": ">=1.30.0",
"node": ">=8.0.0"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wellwelwel"
}
},
"node_modules/make-dir": { "node_modules/make-dir": {
"version": "3.1.0", "version": "3.1.0",
"dev": true, "dev": true,
@ -4423,47 +4381,6 @@
"safe-buffer": "^5.1.2" "safe-buffer": "^5.1.2"
} }
}, },
"node_modules/mysql2": {
"version": "3.17.4",
"license": "MIT",
"dependencies": {
"aws-ssl-profiles": "^1.1.2",
"denque": "^2.1.0",
"generate-function": "^2.3.1",
"iconv-lite": "^0.7.2",
"long": "^5.3.2",
"lru.min": "^1.1.4",
"named-placeholders": "^1.1.6",
"sql-escaper": "^1.3.3"
},
"engines": {
"node": ">= 8.0"
}
},
"node_modules/mysql2/node_modules/iconv-lite": {
"version": "0.7.2",
"license": "MIT",
"dependencies": {
"safer-buffer": ">= 2.1.2 < 3.0.0"
},
"engines": {
"node": ">=0.10.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/express"
}
},
"node_modules/named-placeholders": {
"version": "1.1.6",
"license": "MIT",
"dependencies": {
"lru.min": "^1.1.0"
},
"engines": {
"node": ">=8.0.0"
}
},
"node_modules/nanoid": { "node_modules/nanoid": {
"version": "2.1.11", "version": "2.1.11",
"license": "MIT" "license": "MIT"
@ -5154,6 +5071,87 @@
"version": "2.1.0", "version": "2.1.0",
"license": "MIT" "license": "MIT"
}, },
"node_modules/pg": {
"version": "8.18.0",
"resolved": "https://registry.npmjs.org/pg/-/pg-8.18.0.tgz",
"integrity": "sha512-xqrUDL1b9MbkydY/s+VZ6v+xiMUmOUk7SS9d/1kpyQxoJ6U9AO1oIJyUWVZojbfe5Cc/oluutcgFG4L9RDP1iQ==",
"dependencies": {
"pg-connection-string": "^2.11.0",
"pg-pool": "^3.11.0",
"pg-protocol": "^1.11.0",
"pg-types": "2.2.0",
"pgpass": "1.0.5"
},
"engines": {
"node": ">= 16.0.0"
},
"optionalDependencies": {
"pg-cloudflare": "^1.3.0"
},
"peerDependencies": {
"pg-native": ">=3.0.1"
},
"peerDependenciesMeta": {
"pg-native": {
"optional": true
}
}
},
"node_modules/pg-cloudflare": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.3.0.tgz",
"integrity": "sha512-6lswVVSztmHiRtD6I8hw4qP/nDm1EJbKMRhf3HCYaqud7frGysPv7FYJ5noZQdhQtN2xJnimfMtvQq21pdbzyQ==",
"optional": true
},
"node_modules/pg-connection-string": {
"version": "2.11.0",
"resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.11.0.tgz",
"integrity": "sha512-kecgoJwhOpxYU21rZjULrmrBJ698U2RxXofKVzOn5UDj61BPj/qMb7diYUR1nLScCDbrztQFl1TaQZT0t1EtzQ=="
},
"node_modules/pg-int8": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz",
"integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==",
"engines": {
"node": ">=4.0.0"
}
},
"node_modules/pg-pool": {
"version": "3.11.0",
"resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.11.0.tgz",
"integrity": "sha512-MJYfvHwtGp870aeusDh+hg9apvOe2zmpZJpyt+BMtzUWlVqbhFmMK6bOBXLBUPd7iRtIF9fZplDc7KrPN3PN7w==",
"peerDependencies": {
"pg": ">=8.0"
}
},
"node_modules/pg-protocol": {
"version": "1.11.0",
"resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.11.0.tgz",
"integrity": "sha512-pfsxk2M9M3BuGgDOfuy37VNRRX3jmKgMjcvAcWqNDpZSf4cUmv8HSOl5ViRQFsfARFn0KuUQTgLxVMbNq5NW3g=="
},
"node_modules/pg-types": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz",
"integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==",
"dependencies": {
"pg-int8": "1.0.1",
"postgres-array": "~2.0.0",
"postgres-bytea": "~1.0.0",
"postgres-date": "~1.0.4",
"postgres-interval": "^1.1.0"
},
"engines": {
"node": ">=4"
}
},
"node_modules/pgpass": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz",
"integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==",
"dependencies": {
"split2": "^4.1.0"
}
},
"node_modules/picomatch": { "node_modules/picomatch": {
"version": "2.3.0", "version": "2.3.0",
"dev": true, "dev": true,
@ -5224,6 +5222,41 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/postgres-array": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz",
"integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==",
"engines": {
"node": ">=4"
}
},
"node_modules/postgres-bytea": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.1.tgz",
"integrity": "sha512-5+5HqXnsZPE65IJZSMkZtURARZelel2oXUEO8rH83VS/hxH5vv1uHquPg5wZs8yMAfdv971IU+kcPUczi7NVBQ==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/postgres-date": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz",
"integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/postgres-interval": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz",
"integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==",
"dependencies": {
"xtend": "^4.0.0"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/prelude-ls": { "node_modules/prelude-ls": {
"version": "1.2.1", "version": "1.2.1",
"dev": true, "dev": true,
@ -5951,23 +5984,18 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/split2": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz",
"integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==",
"engines": {
"node": ">= 10.x"
}
},
"node_modules/sprintf-js": { "node_modules/sprintf-js": {
"version": "1.0.3", "version": "1.0.3",
"license": "BSD-3-Clause" "license": "BSD-3-Clause"
}, },
"node_modules/sql-escaper": {
"version": "1.3.3",
"license": "MIT",
"engines": {
"bun": ">=1.0.0",
"deno": ">=2.0.0",
"node": ">=12.0.0"
},
"funding": {
"type": "github",
"url": "https://github.com/mysqljs/sql-escaper?sponsor=1"
}
},
"node_modules/sshpk": { "node_modules/sshpk": {
"version": "1.16.1", "version": "1.16.1",
"license": "MIT", "license": "MIT",
@ -6686,6 +6714,14 @@
"version": "2.0.3", "version": "2.0.3",
"license": "Apache-2.0" "license": "Apache-2.0"
}, },
"node_modules/xtend": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
"engines": {
"node": ">=0.4"
}
},
"node_modules/y18n": { "node_modules/y18n": {
"version": "5.0.8", "version": "5.0.8",
"dev": true, "dev": true,

View File

@ -54,7 +54,7 @@
"@loopback/rest-explorer": "^3.3.1", "@loopback/rest-explorer": "^3.3.1",
"@loopback/service-proxy": "^3.2.1", "@loopback/service-proxy": "^3.2.1",
"loopback-connector-rest": "^3.7.0", "loopback-connector-rest": "^3.7.0",
"mysql2": "^3.11.0", "pg": "^8.18.0",
"tslib": "^2.0.0" "tslib": "^2.0.0"
}, },
"devDependencies": { "devDependencies": {

View File

@ -1,20 +1,30 @@
const http = require("http"); const http = require("http");
const { URL } = require("url"); const { URL } = require("url");
const mysql = require("mysql2/promise"); const { Pool } = require("pg");
const pool = process.env.DATABASE_URL function buildPgConfig() {
? mysql.createPool(process.env.DATABASE_URL) if (process.env.DATABASE_URL) {
: mysql.createPool({ return {
host: process.env.DB_HOST, connectionString: process.env.DATABASE_URL,
port: process.env.DB_PORT || "3306", ssl: process.env.PGSSLMODE === "require" ? { rejectUnauthorized: false } : undefined,
user: process.env.DB_USER, };
password: process.env.DB_PASSWORD, }
database: process.env.DB_NAME || "postgres",
}); return {
host: process.env.PGHOST,
port: Number(process.env.PGPORT || "5432"),
user: process.env.PGUSER,
password: process.env.PGPASSWORD,
database: process.env.PGDATABASE || "postgres",
ssl: process.env.PGSSLMODE === "require" ? { rejectUnauthorized: false } : undefined,
};
}
const pool = new Pool(buildPgConfig());
async function ensureTable() { async function ensureTable() {
await pool.query( await pool.query(
"CREATE TABLE IF NOT EXISTS nubes_test_table (id INT AUTO_INCREMENT PRIMARY KEY, test_data TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP)" "CREATE TABLE IF NOT EXISTS nubes_test_table (id SERIAL PRIMARY KEY, test_data TEXT, created_at TIMESTAMP DEFAULT NOW())"
); );
} }
@ -56,10 +66,10 @@ function renderPage(rows, error) {
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<title>Node + MariaDB Demo</title> <title>NodeJS + Postgres Demo</title>
</head> </head>
<body> <body>
<h3>Node + MariaDB Demo</h3> <h3>NodeJS + Postgres Demo</h3>
${errorHtml} ${errorHtml}
<form method="POST" action="/add"> <form method="POST" action="/add">
<input type="text" name="txt_content" placeholder="New message"> <input type="text" name="txt_content" placeholder="New message">
@ -85,7 +95,7 @@ async function handleRequest(req, res) {
if (req.method === "GET" && url.pathname === "/") { if (req.method === "GET" && url.pathname === "/") {
try { try {
await ensureTable(); await ensureTable();
const [rows] = await pool.query( const { rows } = await pool.query(
"SELECT id, test_data FROM nubes_test_table ORDER BY id DESC LIMIT 20" "SELECT id, test_data FROM nubes_test_table ORDER BY id DESC LIMIT 20"
); );
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" }); res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
@ -108,16 +118,16 @@ async function handleRequest(req, res) {
await ensureTable(); await ensureTable();
if (url.pathname === "/add") { if (url.pathname === "/add") {
const content = form.txt_content || "Node did it"; const content = form.txt_content || "Node did it";
await pool.query("INSERT INTO nubes_test_table (test_data) VALUES (?)", [content]); await pool.query("INSERT INTO nubes_test_table (test_data) VALUES ($1)", [content]);
} }
if (url.pathname === "/update") { if (url.pathname === "/update") {
await pool.query("UPDATE nubes_test_table SET test_data=? WHERE id=?", [ await pool.query("UPDATE nubes_test_table SET test_data=$1 WHERE id=$2", [
form.txt_content || "", form.txt_content || "",
form.id, form.id,
]); ]);
} }
if (url.pathname === "/delete") { if (url.pathname === "/delete") {
await pool.query("DELETE FROM nubes_test_table WHERE id=?", [form.id]); await pool.query("DELETE FROM nubes_test_table WHERE id=$1", [form.id]);
} }
res.writeHead(303, { Location: "/" }); res.writeHead(303, { Location: "/" });
res.end(); res.end();