|
1 |
| -# Node, Express and PostgreSQL |
2 |
| - |
3 |
| -[](http://standardjs.com) |
4 |
| -[](https://opensource.org/licenses/MIT) |
| 1 | +# Node, Express and PostgreSQL with generic controllers |
5 | 2 |
|
6 | 3 | ## Overview
|
7 | 4 |
|
8 |
| -This is an easy, basic and raw example of **HOW to** implement an API with Node, Express and PostgreSQL (with Sequelize ORM). |
9 |
| - |
10 |
| -## Requirements |
11 |
| - |
12 |
| -- Node 12+ |
13 |
| -- NPM |
14 |
| -- PostgreSQL |
15 |
| -- Sequelize ORM |
16 |
| -- Optional: ElephantSQL account |
17 |
| - |
18 |
| -## Install dependencies |
19 |
| - |
20 |
| -To avoid issues with `husky`, first enable `git hooks` (and add our hook): |
21 |
| - |
22 |
| -```shell |
23 |
| -npx husky install |
24 |
| - |
25 |
| -npx husky add .husky/pre-commit |
26 |
| -``` |
27 |
| - |
28 |
| -Then, install the dependencies as usual: |
29 |
| - |
30 |
| -```shell |
31 |
| -npm install |
32 |
| -``` |
33 |
| - |
34 |
| -## DB |
35 |
| - |
36 |
| -### Create database |
37 |
| - |
38 |
| -```shell |
39 |
| -createdb users |
40 |
| -``` |
41 |
| - |
42 |
| -### Populate data |
43 |
| - |
44 |
| -```shell |
45 |
| -psql users |
46 |
| -``` |
47 |
| - |
48 |
| -#### Add data to users table |
49 |
| - |
50 |
| -```sql |
51 |
| -COPY users(id, firstname, lastname, age, gender, username, company, email, phone, address, created_at, updated_at) |
52 |
| -FROM '/Users/your-user/data/node-express-postgresql/users.csv' |
53 |
| -DELIMITER ',' |
54 |
| -CSV HEADER; |
55 |
| -``` |
56 |
| - |
57 |
| -### Dump data from local DB to external |
58 |
| - |
59 |
| -```shell |
60 |
| -pg_dump postgres://your-user: [email protected]/agency | psql postgres://your-user: [email protected]/your-database-name |
61 |
| -``` |
62 |
| - |
63 |
| -## Running the server |
64 |
| - |
65 |
| -### Development |
66 |
| - |
67 |
| -```shell |
68 |
| -npm run dev |
69 |
| -``` |
70 |
| - |
71 |
| -### Production |
72 |
| - |
73 |
| -```shell |
74 |
| -npm run build |
75 |
| - |
76 |
| -npm start |
77 |
| -``` |
78 |
| - |
79 |
| -## API endpoints |
80 |
| - |
81 |
| -### GET /api/users |
82 |
| - |
83 |
| -- Returns an object with the key data containing an array of objects with `40 records`. |
84 |
| -- Supports query string: |
85 |
| - - ?limit=integer |
86 |
| - - ?offset=integer |
87 |
| - |
88 |
| -#### Request: |
89 |
| - |
90 |
| -```shell |
91 |
| -curl http://127.0.0.1:3333/api/users |
92 |
| -``` |
93 |
| - |
94 |
| -#### Sample response: |
95 |
| - |
96 |
| -```json |
97 |
| -{ |
98 |
| - "data": [ |
99 |
| - { |
100 |
| - "id": 1, |
101 |
| - "firstname": "Christian", |
102 |
| - "lastname": "Deackes", |
103 |
| - "age": 36, |
104 |
| - "gender": "Genderqueer", |
105 |
| - "username": "cdeackes0", |
106 |
| - "company": "Eayo", |
107 |
| - |
108 |
| - "phone": "602-240-5463", |
109 |
| - "address": "53 Lakewood Plaza", |
110 |
| - "createdAt": "2020-11-30T08:00:00.000Z", |
111 |
| - "updatedAt": "2021-03-28T07:00:00.000Z" |
112 |
| - }, |
113 |
| - { |
114 |
| - "id": 2, |
115 |
| - "firstname": "Staford", |
116 |
| - "lastname": "Noice", |
117 |
| - "age": 27, |
118 |
| - "gender": "Female", |
119 |
| - "username": "snoice1", |
120 |
| - "company": "Oyoloo", |
121 |
| - |
122 |
| - "phone": "951-811-1800", |
123 |
| - "address": "18298 Crest Line Road", |
124 |
| - "createdAt": "2021-06-30T07:00:00.000Z", |
125 |
| - "updatedAt": "2021-07-14T07:00:00.000Z" |
126 |
| - } |
127 |
| - ] |
128 |
| -} |
129 |
| -``` |
130 |
| - |
131 |
| -#### Query string |
| 5 | +This is an easy, basic and raw example of **HOW to** implement an API with Node, Express and PostgreSQL (with Sequelize ORM) **with generic controllers**. |
132 | 6 |
|
133 |
| -##### GET /api/users?limit=1 |
| 7 | +This project is based on the template `node-express-postgresql`. Check out the repo [README](../node-express-postgresql/README.md) for setup and development. |
134 | 8 |
|
135 |
| -- Returns `n` record(s) where `n` is the value (type: Number) of the `limit` key. |
| 9 | +--- |
136 | 10 |
|
137 |
| -###### Request: |
| 11 | +## CRUD Generic Controllers |
138 | 12 |
|
139 |
| -``` |
140 |
| -curl http://127.0.0.1:3333/api/users?limit=1 |
141 |
| -``` |
| 13 | +### Using the Generic Controllers |
142 | 14 |
|
143 |
| -###### Response: |
| 15 | +We are going to use our `generic controllers` to perform common `CRUD operations`, giving a model: |
| 16 | +* Create a document in a collection (POST) |
| 17 | +* Retrieve all the documents in a collection or one document by its ID (GET) |
| 18 | +* Update a document in a collection by its ID (PUT) |
| 19 | +* Delete a document in a collection by its ID (DELETE) |
144 | 20 |
|
145 |
| -```json |
146 |
| -{ |
147 |
| - "data": [ |
148 |
| - { |
149 |
| - "id": 1, |
150 |
| - "firstname": "Christian", |
151 |
| - "lastname": "Deackes", |
152 |
| - "age": 36, |
153 |
| - "gender": "Genderqueer", |
154 |
| - "username": "cdeackes0", |
155 |
| - "company": "Eayo", |
156 |
| - |
157 |
| - "phone": "602-240-5463", |
158 |
| - "address": "53 Lakewood Plaza", |
159 |
| - "createdAt": "2020-11-30T08:00:00.000Z", |
160 |
| - "updatedAt": "2021-03-28T07:00:00.000Z" |
161 |
| - }, |
162 |
| - ] |
163 |
| -} |
164 |
| -``` |
| 21 | +If you need to create a custom controller for a particular resource, add it to `/src/resources/collection/controllers.js` and update the router with the route and the exported method (controller) `/src/resources/collection/router.js` |
165 | 22 |
|
166 |
| -Wrong type for `n` value will return _all the users_. |
167 |
| -Example: `users?limit=%27Hello%27` |
| 23 | +### Controllers and Side Effects |
168 | 24 |
|
169 |
| -##### GET /api/users?offset=10 |
| 25 | +For operations involving `side effects`, or, everything outside the scope of retrieving (GET) you should definitely need authentication and authorization before executing the operation. You don't want random people creating, updating, deleting your resources. This should be obvious, however, if you are using this a template be extremely careful since we are not protecting endpoints (it is not the goal of this material). |
170 | 26 |
|
171 |
| -- Returns from `n` (PRIMARY KEY) where `n` is the value (type: Number) of the `offset` key. |
| 27 | +Once you have the proper logic in place to allow certain entities to perform CRUD operations, and, if you are going to use `generic controllers`, just chain the method and pass the imported controller in your `/src/resources/collection/router.js` |
172 | 28 |
|
173 |
| -###### Request: |
| 29 | +So, as an example, if you want to support the creation of a new application in the applications collection your `/src/resources/applications/router.js` would look like: |
174 | 30 |
|
175 |
| -``` |
176 |
| -curl http://127.0.0.1:3333/api/users?offset=10 |
| 31 | +```js |
| 32 | +router.route('/').get(controllers.getSomeOrAll) |
| 33 | + .post(controllers.createOne) |
177 | 34 | ```
|
178 | 35 |
|
179 |
| -###### Response: |
| 36 | +If you want to allow, updating and deleting an application in the applications collection: |
180 | 37 |
|
181 |
| -```json |
182 |
| -{ |
183 |
| - "data": [ |
184 |
| - { |
185 |
| - "id": 11, |
186 |
| - "firstname": "Goldie", |
187 |
| - "lastname": "Dany", |
188 |
| - "age": 88, |
189 |
| - "gender": "Female", |
190 |
| - "username": "gdanya", |
191 |
| - "company": "Devcast", |
192 |
| - |
193 |
| - "phone": "954-161-7922", |
194 |
| - "address": "68 Drewry Plaza", |
195 |
| - "createdAt": "2021-03-28T07:00:00.000Z", |
196 |
| - "updatedAt": "2021-03-19T07:00:00.000Z" |
197 |
| - }, |
198 |
| - { |
199 |
| - "id": 12, |
200 |
| - "firstname": "Kial", |
201 |
| - "lastname": "Hamberstone", |
202 |
| - "age": 53, |
203 |
| - "gender": "Male", |
204 |
| - "username": "khamberstoneb", |
205 |
| - "company": "Skipfire", |
206 |
| - |
207 |
| - "phone": "896-244-3662", |
208 |
| - "address": "68425 Buell Point", |
209 |
| - "createdAt": "2020-10-11T07:00:00.000Z", |
210 |
| - "updatedAt": "2021-06-02T07:00:00.000Z" |
211 |
| - } |
212 |
| - ] |
213 |
| -} |
| 38 | +```js |
| 39 | +router.route('/:id').get(controllers.getOne) |
| 40 | + .put(controllers.updateOne) |
| 41 | + .delete(controllers.deleteOne) |
214 | 42 | ```
|
215 |
| - |
216 |
| -### GET /latency |
217 |
| - |
218 |
| -- Returns an object with a delay of 1 second (default) |
219 |
| -- Supports query string: |
220 |
| - - ?limit=integer |
221 |
| - - ?offset=integer |
222 |
| - |
223 |
| -#### Request: |
224 |
| - |
225 |
| -``` |
226 |
| -curl http://127.0.0.1:3333/latency |
227 |
| -``` |
228 |
| - |
229 |
| -#### Response: |
230 |
| - |
231 |
| -```json |
232 |
| -{ |
233 |
| - "data": "Thanks for waiting 1 second" |
234 |
| -} |
235 |
| -``` |
236 |
| - |
237 |
| -#### Query string |
238 |
| - |
239 |
| -##### GET /latency?delay=2000 |
240 |
| - |
241 |
| -- Increases latency (delay) to `n` milliseconds where, _min:1000_ and _max:4000_. Default value: 1000ms. |
242 |
| - |
243 |
| -Wrong type for `n` value will produce a default delay of 1000ms. |
244 |
| - |
245 |
| -###### Request: |
246 |
| - |
247 |
| -``` |
248 |
| -curl http://127.0.0.1:3333/latency?delay=2000 |
249 |
| -``` |
250 |
| - |
251 |
| -###### Response: |
252 |
| - |
253 |
| -```json |
254 |
| -{ |
255 |
| - "data": "Thanks for waiting 2 seconds" |
256 |
| -} |
257 |
| -``` |
258 |
| - |
259 |
| -### GET everything else |
260 |
| - |
261 |
| -- Any other endpoint will retrieve an object |
262 |
| - |
263 |
| -#### Request: |
264 |
| - |
265 |
| -``` |
266 |
| -curl http://127.0.0.1:3333/ |
267 |
| -``` |
268 |
| - |
269 |
| -#### Response: |
270 |
| - |
271 |
| -```json |
272 |
| -{ |
273 |
| - "message": "Node.js, Express, and PostgreSQL API!" |
274 |
| -} |
275 |
| -``` |
0 commit comments