From 6e8edaad61156f163e946a40c2c554b8870a3723 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CNaeel=E2=80=9D?= Date: Sun, 22 Feb 2026 16:14:53 +0400 Subject: [PATCH] Switch demo to Postgres --- README.md | 2 +- package-lock.json | 230 +++++++++++++++++++++++++++------------------- package.json | 2 +- server.js | 44 +++++---- 4 files changed, 162 insertions(+), 116 deletions(-) diff --git a/README.md b/README.md index f13d7f0..14f8663 100644 --- a/README.md +++ b/README.md @@ -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. If the input is empty, it inserts "Node did it". diff --git a/package-lock.json b/package-lock.json index 95fdcb4..4f04357 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,7 +15,7 @@ "@loopback/rest-explorer": "^3.3.1", "@loopback/service-proxy": "^3.2.1", "loopback-connector-rest": "^3.7.0", - "mysql2": "^3.11.0", + "pg": "^8.18.0", "tslib": "^2.0.0" }, "devDependencies": { @@ -1479,13 +1479,6 @@ "node": "*" } }, - "node_modules/aws-ssl-profiles": { - "version": "1.1.2", - "license": "MIT", - "engines": { - "node": ">= 6.0.0" - } - }, "node_modules/aws4": { "version": "1.11.0", "license": "MIT" @@ -2035,13 +2028,6 @@ "node": ">=0.4.0" } }, - "node_modules/denque": { - "version": "2.1.0", - "license": "Apache-2.0", - "engines": { - "node": ">=0.10" - } - }, "node_modules/depd": { "version": "2.0.0", "license": "MIT", @@ -2964,13 +2950,6 @@ "dev": true, "license": "MIT" }, - "node_modules/generate-function": { - "version": "2.3.1", - "license": "MIT", - "dependencies": { - "is-property": "^1.0.2" - } - }, "node_modules/gensync": { "version": "1.0.0-beta.2", "dev": true, @@ -3500,10 +3479,6 @@ "node": ">=8" } }, - "node_modules/is-property": { - "version": "1.0.2", - "license": "MIT" - }, "node_modules/is-regex": { "version": "1.1.3", "license": "MIT", @@ -4037,10 +4012,6 @@ "node": ">=8" } }, - "node_modules/long": { - "version": "5.3.2", - "license": "Apache-2.0" - }, "node_modules/loopback-connector": { "version": "5.0.1", "license": "MIT", @@ -4151,19 +4122,6 @@ "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": { "version": "3.1.0", "dev": true, @@ -4423,47 +4381,6 @@ "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": { "version": "2.1.11", "license": "MIT" @@ -5154,6 +5071,87 @@ "version": "2.1.0", "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": { "version": "2.3.0", "dev": true, @@ -5224,6 +5222,41 @@ "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": { "version": "1.2.1", "dev": true, @@ -5951,23 +5984,18 @@ "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": { "version": "1.0.3", "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": { "version": "1.16.1", "license": "MIT", @@ -6686,6 +6714,14 @@ "version": "2.0.3", "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": { "version": "5.0.8", "dev": true, diff --git a/package.json b/package.json index 37908fd..fa2e5d5 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,7 @@ "@loopback/rest-explorer": "^3.3.1", "@loopback/service-proxy": "^3.2.1", "loopback-connector-rest": "^3.7.0", - "mysql2": "^3.11.0", + "pg": "^8.18.0", "tslib": "^2.0.0" }, "devDependencies": { diff --git a/server.js b/server.js index ee78cf7..8d72747 100644 --- a/server.js +++ b/server.js @@ -1,20 +1,30 @@ const http = require("http"); const { URL } = require("url"); -const mysql = require("mysql2/promise"); +const { Pool } = require("pg"); -const pool = process.env.DATABASE_URL - ? mysql.createPool(process.env.DATABASE_URL) - : mysql.createPool({ - host: process.env.DB_HOST, - port: process.env.DB_PORT || "3306", - user: process.env.DB_USER, - password: process.env.DB_PASSWORD, - database: process.env.DB_NAME || "postgres", - }); +function buildPgConfig() { + if (process.env.DATABASE_URL) { + return { + connectionString: process.env.DATABASE_URL, + ssl: process.env.PGSSLMODE === "require" ? { rejectUnauthorized: false } : undefined, + }; + } + + 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() { 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) { - Node + MariaDB Demo + NodeJS + Postgres Demo -

Node + MariaDB Demo

+

NodeJS + Postgres Demo

${errorHtml}
@@ -85,7 +95,7 @@ async function handleRequest(req, res) { if (req.method === "GET" && url.pathname === "/") { try { 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" ); res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" }); @@ -108,16 +118,16 @@ async function handleRequest(req, res) { await ensureTable(); if (url.pathname === "/add") { 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") { - 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.id, ]); } 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.end();