From 2ae4fb94ccc6fba34e7fcc0f3114f66a108b82ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Hubert?= Date: Tue, 7 Jan 2020 10:49:48 +0100 Subject: [PATCH 1/6] Implement POST /api/users route --- index.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/index.js b/index.js index 44d0503..ec12d27 100644 --- a/index.js +++ b/index.js @@ -25,6 +25,22 @@ app.get('/api/users', (req, res) => { }); }); +app.post('/api/users', (req, res) => { + // send an SQL query to get all users + connection.query('INSERT INTO user SET ?', req.body, (err, results) => { + if (err) { + // If an error has occurred, then the client is informed of the error + res.status(500).json({ + error: err.message, + sql: err.sql, + }); + } else { + // If everything went well, we send the result of the SQL query as JSON + res.json(results); + } + }); +}); + app.listen(process.env.PORT, (err) => { if (err) { throw new Error('Something bad happened...'); From 804aaea71aa7571dd976d3408e3c1007789f0618 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Hubert?= Date: Tue, 7 Jan 2020 13:41:12 +0100 Subject: [PATCH 2/6] Validate inputs with express-validator --- index.js | 24 ++++++++++++++++++------ package-lock.json | 17 +++++++++++++++-- package.json | 1 + 3 files changed, 34 insertions(+), 8 deletions(-) diff --git a/index.js b/index.js index ec12d27..93ef978 100644 --- a/index.js +++ b/index.js @@ -2,6 +2,7 @@ require('dotenv').config(); const express = require('express'); const bodyParser = require('body-parser'); +const { check, validationResult } = require('express-validator'); const connection = require('./db'); const app = express(); @@ -25,19 +26,30 @@ app.get('/api/users', (req, res) => { }); }); -app.post('/api/users', (req, res) => { +app.post('/api/users', [ + // email must be valid + check('email').isEmail(), + // password must be at least 8 chars long + check('password').isLength({ min: 8 }), + // let's assume a name should be 2 chars long + check('name').isLength({ min: 2 }), +], +(req, res) => { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(422).json({ errors: errors.array() }); + } // send an SQL query to get all users - connection.query('INSERT INTO user SET ?', req.body, (err, results) => { + return connection.query('INSERT INTO user SET ?', req.body, (err, results) => { if (err) { // If an error has occurred, then the client is informed of the error - res.status(500).json({ + return res.status(500).json({ error: err.message, sql: err.sql, }); - } else { - // If everything went well, we send the result of the SQL query as JSON - res.json(results); } + // If everything went well, we send the result of the SQL query as JSON + return res.json(results); }); }); diff --git a/package-lock.json b/package-lock.json index 497a612..6390dde 100644 --- a/package-lock.json +++ b/package-lock.json @@ -863,6 +863,15 @@ "vary": "~1.1.2" } }, + "express-validator": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/express-validator/-/express-validator-6.3.1.tgz", + "integrity": "sha512-YQHQKP/zlUTN6d38uWwXgK3At5phK6R24pOB/ImWisMUz/U/1AC3ZXMgiZYhtH4ViYJ6UAiV0/nj8s1Qs3kmvw==", + "requires": { + "lodash": "^4.17.15", + "validator": "^11.1.0" + } + }, "external-editor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", @@ -1413,8 +1422,7 @@ "lodash": { "version": "4.17.15", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", - "dev": true + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" }, "lowercase-keys": { "version": "1.0.1", @@ -2561,6 +2569,11 @@ "spdx-expression-parse": "^3.0.0" } }, + "validator": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-11.1.0.tgz", + "integrity": "sha512-qiQ5ktdO7CD6C/5/mYV4jku/7qnqzjrxb3C/Q5wR3vGGinHTgJZN/TdFT3ZX4vXhX2R1PXx42fB1cn5W+uJ4lg==" + }, "vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", diff --git a/package.json b/package.json index fa9b1df..3d337a8 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "body-parser": "^1.19.0", "dotenv": "^8.2.0", "express": "^4.17.1", + "express-validator": "^6.3.1", "mysql": "^2.17.1" }, "devDependencies": { From 175d95a2acdfb60b7f6b84e633619d30d2d5763f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Hubert?= Date: Fri, 10 Jan 2020 10:33:25 +0100 Subject: [PATCH 3/6] Return the new user as res. body, with 201 and Location header --- index.js | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index 93ef978..dd431fd 100644 --- a/index.js +++ b/index.js @@ -48,8 +48,28 @@ app.post('/api/users', [ sql: err.sql, }); } - // If everything went well, we send the result of the SQL query as JSON - return res.json(results); + // We use the insertId attribute of results to build the WHERE clause + return connection.query('SELECT * FROM user WHERE id = ?', results.insertId, (err2, records) => { + if (err2) { + return res.status(500).json({ + error: err2.message, + sql: err2.sql, + }); + } + // If all went well, records is an array, from which we use the 1st item + const insertedUser = records[0]; + // Extract all the fields *but* password as a new object (user) + const { password, ...user } = insertedUser; + // Get the host + port (localhost:3000) from the request headers + const host = req.get('host'); + // Compute the full location, e.g. http://localhost:3000/api/users/132 + // This will help the client know where the new resource can be found! + const location = `http://${host}${req.url}/${user.id}`; + return res + .status(201) + .set('Location', location) + .json(user); + }); }); }); From f942f16e007762310f0145f517c34a4db9b4370f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Hubert?= Date: Fri, 10 Jan 2020 10:43:22 +0100 Subject: [PATCH 4/6] Reformat/indent --- index.js | 85 +++++++++++++++++++++++++++++--------------------------- 1 file changed, 44 insertions(+), 41 deletions(-) diff --git a/index.js b/index.js index dd431fd..120e764 100644 --- a/index.js +++ b/index.js @@ -26,52 +26,55 @@ app.get('/api/users', (req, res) => { }); }); -app.post('/api/users', [ - // email must be valid - check('email').isEmail(), - // password must be at least 8 chars long - check('password').isLength({ min: 8 }), - // let's assume a name should be 2 chars long - check('name').isLength({ min: 2 }), -], -(req, res) => { - const errors = validationResult(req); - if (!errors.isEmpty()) { - return res.status(422).json({ errors: errors.array() }); - } - // send an SQL query to get all users - return connection.query('INSERT INTO user SET ?', req.body, (err, results) => { - if (err) { - // If an error has occurred, then the client is informed of the error - return res.status(500).json({ - error: err.message, - sql: err.sql, - }); +app.post( + '/api/users', + [ + // email must be valid + check('email').isEmail(), + // password must be at least 8 chars long + check('password').isLength({ min: 8 }), + // let's assume a name should be 2 chars long + check('name').isLength({ min: 2 }), + ], + (req, res) => { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(422).json({ errors: errors.array() }); } - // We use the insertId attribute of results to build the WHERE clause - return connection.query('SELECT * FROM user WHERE id = ?', results.insertId, (err2, records) => { - if (err2) { + // send an SQL query to get all users + return connection.query('INSERT INTO user SET ?', req.body, (err, results) => { + if (err) { + // If an error has occurred, then the client is informed of the error return res.status(500).json({ - error: err2.message, - sql: err2.sql, + error: err.message, + sql: err.sql, }); } - // If all went well, records is an array, from which we use the 1st item - const insertedUser = records[0]; - // Extract all the fields *but* password as a new object (user) - const { password, ...user } = insertedUser; - // Get the host + port (localhost:3000) from the request headers - const host = req.get('host'); - // Compute the full location, e.g. http://localhost:3000/api/users/132 - // This will help the client know where the new resource can be found! - const location = `http://${host}${req.url}/${user.id}`; - return res - .status(201) - .set('Location', location) - .json(user); + // We use the insertId attribute of results to build the WHERE clause + return connection.query('SELECT * FROM user WHERE id = ?', results.insertId, (err2, records) => { + if (err2) { + return res.status(500).json({ + error: err2.message, + sql: err2.sql, + }); + } + // If all went well, records is an array, from which we use the 1st item + const insertedUser = records[0]; + // Extract all the fields *but* password as a new object (user) + const { password, ...user } = insertedUser; + // Get the host + port (localhost:3000) from the request headers + const host = req.get('host'); + // Compute the full location, e.g. http://localhost:3000/api/users/132 + // This will help the client know where the new resource can be found! + const location = `http://${host}${req.url}/${user.id}`; + return res + .status(201) + .set('Location', location) + .json(user); + }); }); - }); -}); + }, +); app.listen(process.env.PORT, (err) => { if (err) { From 62ecccb2b00524d879a2fb44661dae55a5d49cc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Hubert?= Date: Fri, 10 Jan 2020 10:44:33 +0100 Subject: [PATCH 5/6] Move user data validation middlewares out of POST route --- index.js | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/index.js b/index.js index 120e764..f3cf6f9 100644 --- a/index.js +++ b/index.js @@ -26,16 +26,18 @@ app.get('/api/users', (req, res) => { }); }); +const userValidationMiddlewares = [ + // email must be valid + check('email').isEmail(), + // password must be at least 8 chars long + check('password').isLength({ min: 8 }), + // let's assume a name should be 2 chars long + check('name').isLength({ min: 2 }), +]; + app.post( '/api/users', - [ - // email must be valid - check('email').isEmail(), - // password must be at least 8 chars long - check('password').isLength({ min: 8 }), - // let's assume a name should be 2 chars long - check('name').isLength({ min: 2 }), - ], + userValidationMiddlewares, (req, res) => { const errors = validationResult(req); if (!errors.isEmpty()) { From e149b04d3a2366d5a19ec799665035080436cfbd Mon Sep 17 00:00:00 2001 From: Nicolas Dantas Date: Tue, 8 Dec 2020 19:48:03 +0100 Subject: [PATCH 6/6] challenge finished --- index.js | 39 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/index.js b/index.js index f3cf6f9..711892f 100644 --- a/index.js +++ b/index.js @@ -70,7 +70,7 @@ app.post( // This will help the client know where the new resource can be found! const location = `http://${host}${req.url}/${user.id}`; return res - .status(201) + .status(200) .set('Location', location) .json(user); }); @@ -78,6 +78,43 @@ app.post( }, ); +app.put( + '/api/users/:id', + userValidationMiddlewares, + (req, res) => { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(422).json({ errors: errors.array() }); + } + const { email, password, name } = req.body; + return connection.query('UPDATE user SET email=?, password=?, name=? WHERE id=?', [email, password, name, req.params.id], (err, results) => { + if (err) { + // If an error has occurred, then the client is informed of the error + return res.status(500).json({ + error: err.message, + sql: err.sql, + }); + } + return connection.query('SELECT * FROM user WHERE id = ?', req.params.id, (err2, update) => { + if (err2) { + return res.status(500).json({ + error: err2.message, + sql: err2.sql, + }); + } + const updatedUser = update[0]; + const { password, ...user } = updatedUser; + const host = req.get('host'); + const location = { location: `http://${host}${req.url}` }; + const updatedUserWithLocation = { ...user, ...location }; + return res + .status(200) + .json(updatedUserWithLocation); + }); + }); + }, +); + app.listen(process.env.PORT, (err) => { if (err) { throw new Error('Something bad happened...');