https://github.com/dirkjbosman/node-postgresql-unstructured
An (Unstructured) Express Node PostgreSQL App
https://github.com/dirkjbosman/node-postgresql-unstructured
api express javascript node nodejs postgresql sql
Last synced: about 1 month ago
JSON representation
An (Unstructured) Express Node PostgreSQL App
- Host: GitHub
- URL: https://github.com/dirkjbosman/node-postgresql-unstructured
- Owner: dirkjbosman
- Created: 2020-08-25T07:35:35.000Z (almost 5 years ago)
- Default Branch: master
- Last Pushed: 2020-09-12T13:16:30.000Z (over 4 years ago)
- Last Synced: 2025-05-08T17:57:19.449Z (about 1 month ago)
- Topics: api, express, javascript, node, nodejs, postgresql, sql
- Language: JavaScript
- Homepage:
- Size: 63.5 KB
- Stars: 0
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# node-postgresql-unstructured
#### Summary:
- node express server, middleware and apis
- postgresql relational database### Boilerplates:
- [Part 1: node-postgresql-unstructured](https://github.com/dirkbosman/node-postgresql-unstructured)
- [Part 2: node-postgresql-destructured](https://github.com/dirkbosman/node-postgresql-destructured)
- [Part 3: node-postgresql-controllers](https://github.com/dirkbosman/node-postgresql-controllers)
---
- [Part 4a: node-mongodb-controllers](https://github.com/dirkbosman/node-mongodb-controllers)
- Part 4b: Combine [node-mongodb-clientforserver](https://github.com/dirkbosman/node-mongodb-clientforserver) + [node-mongodb-controllers](https://github.com/dirkbosman/node-mongodb-controllers)# Clone Repo
```
git clone https://github.com/dirkbosman/node-postgresql-unstructured.git
cd node-postgresql-unstructured
npm i
npm start
```# Manual Set-up
## Environment & Libraries
Install the necessary libraries:
```
npm init -y
npm i express
npm i nodemon --save-dev
npm i dotenv
```Add the following to package.json:
```
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "nodemon server.js"
}
```Note: we add nodemon only for development, and not for production, because it is unsafe.
```
"dependencies": {
"dotenv": "^8.2.0",
"express": "^4.17.1"
},
"devDependencies": {
"nodemon": "^2.0.4"
}
```For production, you would configure start-prod in npm-scripts / in docker configurations.
```
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start-prod": "node server.js"
}
```You can run the following in the terminal to start the server (via nodemon):
`npm start`## Database (PostgreSQL)
Navigate to https://api.elephantsql.com/console/ to create an account and a server instance to host your database.
Create your database and copy the information. You will need this latter details to connect via your application.
Create your database tables now:
```
CREATE TABLE users (
id SERIAL PRIMARY KEY,
first_name varchar(255),
last_name varchar(255),
age int
);CREATE TABLE orders (
id SERIAL PRIMARY KEY,
price float,
date timestamp,
user_id int,
FOREIGN KEY (user_id) REFERENCES users(id)
);INSERT INTO users (first_name, last_name, age) VALUES ('John', 'Doe', 18);
INSERT INTO users (first_name, last_name, age) VALUES ('Bob', 'Dylan', 30);
INSERT INTO users (first_name, last_name, age) VALUES ('Jane', 'Doe', 25);INSERT INTO orders (price,date, user_id) VALUES ( 18, '2001-01-01 00:00:00', 1);
INSERT INTO orders (price,date, user_id) VALUES ( 18, '2001-01-02 04:00:00', 1);
INSERT INTO orders (price,date, user_id) VALUES ( 18, '2001-01-03 05:00:00', 2);
INSERT INTO orders (price,date, user_id) VALUES ( 18, '2001-01-04 06:00:00', 2);
```Test whether you get an output under the `Browse`-tab:
- `SELECT * FROM users;`
- `SELECT * FROM orders;`Install the PostgreSQL adapter in Node:
`npm i pg`https://www.elephantsql.com/
https://customer.elephantsql.com/instanceYou can log in with your Github Account
Create a new instance
Add your connection details from your console's [settings](https://api.elephantsql.com/console/) in a `.env`- or `.env.production`-file, as well as add `.env`- or `.env.production` in your `.gitignore`-file. Note the way you use the .env file can depend on your OS, so replicate the syntax that worked for you.
Usually:
- PGUSER => Username
- PGHOST => Server
- PGPASSWORD => PW
- PGDATABASE => Username
- PGPORT => <4-letters>```
PGUSER=
PGHOST=
PGPASSWORD=
PGDATABASE=
PGPORT=
```## Server & APIs Setup (Node & Express)
Read:
- https://expressjs.com/en/guide/routing.html
- https://node-postgres.com
- https://developer.mozilla.org/en-US/docs/Web/HTTP/Status
- https://developer.mozilla.org/en-US/docs/Glossary/REST
- https://developer.mozilla.org/en-US/docs/Web/APICreate routes for the _users_ on:
- `GET` on `/` -> To get all the users.
- `GET` on `/:id` -> To get one user (with the id).
- `POST` on `/` -> To create a new user.
- `PUT` on `/:id` -> To edit one user (with the id).
- `DELETE` on `/:id` -> To delete one user (with the id).Create routes for the _orders_ on:
- `GET` on `/` -> To get all the orders.
- `GET` on `/:id` -> To get one order (with the id).
- `POST` on `/` -> To create a new order.
- `PUT` on `/:id` -> To edit one order (with the id).
- `DELETE` on `/:id` -> To delete one order (with the id).Creating the main file:
```
# express.jsrequire('dotenv').config();
const express = require("express");
const { Pool } = require('pg');const app = express();
const pool = new Pool();app.get("/", (req, res) => res.send("hello world"));
app.listen('3000', () => console.log('connected'));
```Navigate to: `http://localhost:3000/`:
`hello world`Add a new route to server.js:
```
app.get("/users", (req, res) => {
pool
.query("SELECT * FROM users;")
.then((data) => res.json(data.rows))
.catch((e) => res.sendStatus(404));
});
```Navigate to: `http://localhost:3000/users`:
`[{"id":1,"first_name":"John","last_name":"Doe","age":18},{"id":2,"first_name":"Bob","last_name":"Dylan","age":30},{"id":3,"first_name":"Jane","last_name":"Doe","age":25}]`Add a new route to server.js.
- Note: Add a prepared statement(s) "[id]" to protect against SQL injection:
```
app.get("/users/:id", (req, res) => {
const { id } = req.params;
pool
.query("SELECT * FROM users WHERE id=$1;", [id])
.then((data) => res.json(data.rows))
.catch((e) => res.sendStatus(404));
});
```Navigate to: `http://localhost:3000/users/1`:
`[{"id":1,"first_name":"John","last_name":"Doe","age":18}]`Navigate to: `http://localhost:3000/users` vs `http://localhost:3000/users/4`:
- It should give you an empty array, because no values exist in the DB for this id.
`[]`#### SQL Injection:
This is an SQL injection you can easily try as it does not involve a new query and does not kill your database
```
app.get("/users/:id", (req, res) => {
const { id } = req.params;
pool
.query(`SELECT * FROM users WHERE id=${id}`)
.then(data => res.json(data.rows))
.catch(e => res.sendStatus(500));
});
```and your address would be: `http://localhost:3000/users/(SELECT%20MAX(id)%20FROM%20users)`, everything after `http://localhost:3000/users/` is saved under the `id`-param and then executed as a select-statement. The outcome should be: the user with the highest id. Note: the endpoint version in server.js uses prepared statements, so hitting the same url will output an error.
#### SQL Prevention Demo & Protection:
```
app.get("/users/:id", (req, res) => {
const { id } = req.params;
pool
.query(`SELECT * FROM users WHERE id=${id}`)
.then((data) => res.json(data.rows))
.catch((e) => res.sendStatus(500));
});
```#### Middleware:
- Install the middleware:
`npm i body-parser`#### INSERT Record(s):
```
app.post("/users", (req, res) => {
const { first_name, last_name, age } = req.body;pool
.query("INSERT INTO users(first_name, last_name, age) values($1,$2, $3);", [
first_name,
last_name,
age,
])
.then((data) => res.status(201).json(data))
.catch((e) => res.sendStatus(500));
});
```Terminal:
`curl -X POST -H "Content-Type:application/json" http://localhost:3000/users -d '{"first_name":"Piet", "last_name":"Skyf", "age":"17"}'`#### DELETE Record(s):
```
app.delete("/users/:id", (req, res) => {
const { id } = req.params;pool
.query('DELETE FROM users WHERE id=$1;', [id])
.then(data => res.status(201).json(data))
.catch(e => res.sendStatus(500));
});
```Terminal:
- Note: You will not be able to delete a record (via curl) if you have a reference from one record to another table's record in the DB.
`curl -H "Content-Type: application/json" -X DELETE http://localhost:3000/users/3`#### UPDATE Record(s):
```
app.put("/users/:id", (req, res) => {
const { id } = req.params;
const { name } = req.body;
pool
.query('UPDATE users SET first_name=$1 WHERE id=$2;', [name, id])
.then(data => res.status(201).json(data))
.catch(e => res.sendStatus(500));
});
```Terminal:
`curl -d '{"name": "Pikachu"}' -H "Content-Type: application/json" -X PUT http://localhost:3000/users/5`## Appendix
To look whether curl requests were successful, you can for example navigate to:
- http://localhost:3000/users
- http://localhost:3000/ordersFurthermore, you can install the following Google Chrome extension to format JSON for you: `JSON Formatter`.