REST-API (PostgreSQL)¶
Wir zeigen hier nochmal in schneller Zusammenfassung die Erstellung einer REST-API unter Verwendung von Node.js und Express. Dieses Mal wird als Datenbank jedoch PostgreSQL verwendet.
PostgreSQL¶
PostgreSQL können Sie sich selbst lokal installieren oder Sie nutzen den PostgreSQL-Server auf ocean.f4.htw-berlin.de
. Das folgende Beispiel zeigt die Verwendung des Servers ocean.f4.htw-berlin.de
.
Node.js und Express¶
Wir richten zunächst das Grundgerüst des Backends ein.
-
Erstellen eines Node.js-Projektes
backend-pg
, Installieren von Express, dotenv, nodemon, corsmkdir backend-postgresql cd backend-postgresql npm init npm install express npm install nodemon --save-dev npm install dotenv npm install cors
-
package.json
anpassen1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
{ "name": "backend-postgresql", "version": "1.0.0", "description": "", "main": "server.js", "scripts": { "watch": "nodemon ./server.js", "test": "echo \"Error: no test specified\" && exit 1" }, "author": "", "license": "ISC", "dependencies": { "cors": "^2.8.5", "dotenv": "^10.0.0", "express": "^4.17.1" }, "devDependencies": { "nodemon": "^2.0.15" } }
-
server.js
undroutes.js
erstellen1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
const express = require('express'); const cors = require('cors'); require('dotenv').config(); const routes = require('./routes'); const app = express(); const PORT = 4000; app.use(express.json()); app.use(cors()); app.use('/', routes); app.listen(PORT, (error) => { if (error) { console.log(error); } else { console.log(`Server started and listening on port ${PORT} ...`); } })
1 2 3 4 5 6 7 8 9
const express = require('express'); const router = express.Router(); // test router.get('/test', async(req, res) => { res.send({ message: "jetzt mit PostgreSQL" }); }); module.exports = router;
-
Backend starten (ist jetzt Port
4000
!) undhttp://localhost:4000/test
aufrufennpm run watch
PostgreSQL¶
Jetzt erstellen wir die Datenbank und binden sie an.
-
ocean.f4.htw-berlin.de
aufrufen, als Name der Datenbankmembers
eingeben und auf diePostgreSQL
-Karte klicken -
Modul
pg
installieren (siehe hier) sowie.env
unddb.js
im Projektordnerbackend-pg
erstellen:npm i pg
1 2 3 4 5
PGUSER=ihr_account_name PGHOST=psql.f4.htw-berlin.de PGPASSWORD=ihr_passwort PGDATABASE=members PGPORT=5432
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
const pg = require('pg'); const client = new pg.Client({ user: process.env.PGUSER, host: process.env.PGHOST, database: process.env.PGDATABASE, password: process.env.PGPASSWORD, port: process.env.PGPORT, }); client.connect(err => { if (err) { console.log(err); } else { console.log('Connection to DB ...'); } }); module.exports = client;
-
Wir erstellen uns dieses Mal sogar ein Extra-Skript, um die die Datenbank mit Einträgen per Skript zu befüllen: Dazu erstellen wir eine Datei
initdb.js
, in der für die Datenbank eine Tabellemembers
erstellt und diese mit Einträgen befüllt wird. Da es recht aufwendig ist, sehr viele Einträge in die Datenbank mit dempg
-Modul zu schreiben, installieren wir noch ein zusätzliches Paketpg-format
, mit dessen Hilfe es möglich ist, die vielen Werte als ein verschachteltes Array anzugeben. Informationen zupg-format
finden Sie hier.npm i pg-format
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
const express = require('express'); const client = require('./db'); const initdb = express.Router(); var format = require('pg-format'); initdb.get('/', async(req, res) => { // Anlegen der Tabelle members let query = ` DROP TABLE IF EXISTS members; CREATE TABLE members(id serial PRIMARY KEY, firstname VARCHAR(50), lastname VARCHAR(50), email VARCHAR(50)); `; try { await client.query(query) console.log("Table created successfully ...") } catch (err) { console.log(err) } // Befüllen der Tabelle members mit 50 Einträgen const values = [ ["Catherine", "Williams", "cwilliamsl@360.cn"], ["Adam", "Anderson", "aanderson8@google.fr"], ["Susan", "Andrews", "sandrewsn@google.co.jp"], ["Catherine", "Andrews", "candrewsp@noaa.gov"], ["Alan", "Bradley", "abradley1c@globo.com"], ["Anne", "Brooks", "abrooks16@bravesites.com"], ["Russell", "Brown", "rbrownq@nifty.com"], ["Ryan", "Burton", "rburton18@foxnews.com"], ["Roy", "Campbell", "rcampbell1@geocities.com"], ["Russell", "Campbell", "rcampbell17@eventbrite.com"], ["Bonnie", "Coleman", "bcoleman11@fc2.com"], ["Ernest", "Coleman", "ecoleman15@businessweek.com"], ["Richard", "Cruz", "rcruz7@unc.edu"], ["Sean", "Cruz", "scruz10@answers.com"], ["Rebecca", "Cunningham", "rcunninghamd@mac.com"], ["Margaret", "Evans", "mevansh@pcworld.com"], ["Jeffrey", "Ford", "jford14@cnet.com"], ["Andrea", "Gardner", "agardnerv@woothemes.com"], ["Deborah", "George", "dgeorge6@furl.net"], ["Sean", "Gibson", "sgibsony@alexa.com"], ["Virginia", "Graham", "vgrahamk@aol.com"], ["Steven", "Hamilton", "shamiltonu@state.tx.us"], ["Virginia", "Hawkins", "vhawkinsf@ehow.com"], ["Edward", "Hicks", "ehicksc@pcworld.com"], ["Mark", "Johnson", "mjohnsonj@hostgator.com"], ["Ruth", "Jordan", "rjordan1a@smugmug.com"], ["Antonio", "Kim", "akim4@odnoklassniki.ru"], ["Jennifer", "Marshall", "jmarshallt@gnu.org"], ["Eric", "Matthews", "ematthews5@independent.co.uk"], ["Raymond", "Mcdonald", "rmcdonald2@ihg.com"], ["Eric", "Miller", "emillere@creativecommons.org"], ["Jonathan", "Morales", "jmoralesa@ovh.net"], ["Marie", "Morgan", "mmorganb@cloudflare.com"], ["Amanda", "Nelson", "anelson13@indiatimes.com"], ["Lisa", "Olson", "lolsonr@telegraph.co.uk"], ["Alice", "Ortiz", "aortizw@histats.com"], ["Peter", "Phillips", "pphillipss@1688.com"], ["Matthew", "Porter", "mporter9@europa.eu"], ["Tammy", "Ray", "trayx@weather.com"], ["Mark", "Richardson", "mrichardson1d@ihg.com"], ["Joan", "Roberts", "jroberts12@alibaba.com"], ["Kathleen", "Rose", "kroseg@pinterest.com"], ["Steve", "Sanders", "ssanders1b@wikispaces.com"], ["Shirley", "Scott", "sscottm@macromedia.com"], ["Lillian", "Stephens", "lstephens19@hugedomains.com"], ["Nicole", "Thompson", "nthompson3@admin.ch"], ["Marie", "Thompson", "mthompsonz@yelp.com"], ["Alan", "Vasquez", "avasquezo@miibeian.gov.cn"], ["Mildred", "Watkins", "mwatkins0@miibeian.gov.cn"], ["Eugene", "Williams", "ewilliamsi@deliciousdays.com"] ]; const paramquery = format('INSERT INTO members(firstname, lastname, email) VALUES %L RETURNING *', values); try { const result = await client.query(paramquery) console.log("50 members inserted ...") res.status(200) res.send(result.rows) } catch (err) { console.log(err) } }); module.exports = initdb;
Das Skript enthält zwei Anfragen an die Datenbank. Zunächst wird die Tabelle
members
gelöscht, falls sie existiert und dann erstellt. Das Ausführen dieser Anfrage erfolgt in Zeile16
. Danach werden in einer zweiten Anfrage 50 Einträge in diese Tabelle eingefügt. Die Anfrage wird in Zeile75
mithilfe despg-format
-Paketes formatiert (format()
-Methode). Dadurch ist in dem Anfragestring der Platzhalter%L
möglich, in den dann das verschachtelte Arrayvalues
eingesetzt werden kann. Dadurch wird das Einfügen vieler Datensätze erleichtert.Das Skript
initdb.js
wird in derserver.js
verwendet. Unter der Route/init
wird es ausgeführt und alle Datensätze in die Datenbank eingetragen.1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
const express = require('express'); const cors = require('cors'); require('dotenv').config(); const routes = require('./routes'); const init = require('./initdb'); const app = express(); const PORT = 4000; app.use(express.json()); app.use(cors()); app.use('/init', init); app.use('/', routes); app.listen(PORT, (error) => { if (error) { console.log(error); } else { console.log(`Server started and listening on port ${PORT} ...`); } })
Wenn wir nun
http://localhost:4000/init
aufrufen, wird die Datenbank automatisch befüllt.
CRUD-Funktionen¶
Nun fügen wir in die routes.js
die einzelnen Routen ein, um die CRUD-Funktionen zu implementieren. Wir zeigen zunächst die routes.js
vollständig und diskutieren dann einige Aspekte:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
|
Das generelle Prinzip der Anfragen wird für post /members
deutlich (Zeilen 18-33
). Wir definieren uns eine parametrisierte SQL-Anfrage:
const query = `INSERT INTO members(firstname, lastname, email) VALUES ($1, $2, $3) RETURNING *`;
In die Parameter $1
, $2
und $3
werden dann die entsprechenden Werte eingetragen:
const result = await client.query(query, [firstname, lastname, email]);
Wichtig ist, dass die Werte als Array übergeben werden. Der Funktion query()
wird also die parametrisierte Anfrage übergeben und als zweiter Parameter folgt das Array mit den Werten, die in die Parameter eingesetzt werden.
Postman¶
Beachten Sie, dass wir im Unterschied zum Backend mit MongoDB hier die Schlüssel
firstname
,lastname
undemail
verwendet haben. Einer POST
-Anfrage müsste deshalb dem body
beispielsweise folgendes Objekt übergeben werden:
{
"firstname": "Ada",
"lastname": "Lovelace",
"email": "ada@lovelace.uk"
}
Beachten Sie auch, dass in server.js
der Port auf den Wert 4000
gesetzt wurde.
Success
Wir haben nun auch ein Backend, das auf die PostgreSQL zugreift, die Sie auch in der Datenbankvorlesung verwenden. Sie benötigen also keine eigene Installation der PostgreSQL. Ansonsten ist die Struktur und das Konzept des Backends genau das Gleiche wie beim Backend über die MongoDB. Wir haben hier nur auch noch für die Befüllung der Datenbank per Skript gesorgt. Das Backend für die MongoDB ließe sich ebenso entsprechend erweitern.