-
-
Notifications
You must be signed in to change notification settings - Fork 61
West Midlands | 26 March SDC | Iswat Bello | Sprint 3 | Middleware exercises #93
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 7 commits
edd1159
0cfc2d1
8d2a02f
65a83cf
e08d8ec
8402a16
cdcd596
077c598
94c45fa
b33e2f0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| node_modules/ | ||
| package-lock.json | ||
| .env | ||
| .DS_Store | ||
| *.log | ||
| dist/ | ||
| build/ | ||
| .vscode/ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,60 @@ | ||
| import express from "express"; | ||
|
|
||
| const app = express(); | ||
|
|
||
| const PORT = process.env.PORT ? parseInt(process.env.PORT, 10) : 3000; | ||
|
|
||
| const usernameMiddleware = (req, res, next) => { | ||
| const headerValue = req.get("X-username"); | ||
|
|
||
| if (headerValue) { | ||
| req.username = headerValue; | ||
| } else { | ||
| req.username = null; | ||
| } | ||
|
|
||
| next(); | ||
| }; | ||
|
|
||
| app.use(express.json()); | ||
|
|
||
| const validateArray = (req, res, next) => { | ||
| if ( | ||
| req.body && | ||
| Array.isArray(req.body) && | ||
| req.body.every((item) => typeof item === "string") | ||
| ) { | ||
| next(); | ||
| } else { | ||
| res.status(400).send("Error message"); | ||
| } | ||
| }; | ||
|
|
||
| // Hey Express, whenever a POST request arrives at '/', please call this person first (usernameMiddleware), then this person (arrayMiddleware), then finally do the route logic. | ||
| app.post("/", usernameMiddleware, validateArray, (req, res) => { | ||
| let authPart; | ||
| if (req.username) { | ||
| authPart = `You are authenticated as ${req.username}`; | ||
| } else { | ||
| authPart = "You are not authenticated"; | ||
| } | ||
| const MessageCount = req.body.length; | ||
|
|
||
| const messageJoined = req.body.join(","); | ||
|
|
||
| const word = MessageCount === 1 ? "subject" : "subjects"; | ||
|
|
||
| if (MessageCount > 0) { | ||
| res.send( | ||
| `${authPart}\n\nYou have requested information about ${MessageCount} ${word}: ${messageJoined}.`, | ||
| ); | ||
| } else { | ||
| res.send( | ||
| `${authPart}\n\nYou have requested information about ${MessageCount} ${word}.`, | ||
| ); | ||
| } | ||
| }); | ||
|
|
||
| app.listen(PORT, () => { | ||
| console.log("Type your message here"); | ||
| }); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| { | ||
| "name": "middleware-exercise", | ||
| "version": "1.0.0", | ||
| "description": "", | ||
| "main": "index.js", | ||
| "type": "module", | ||
| "scripts": { | ||
| "test": "echo \"Error: no test specified\" && exit 1" | ||
| }, | ||
| "keywords": [], | ||
| "author": "", | ||
| "license": "ISC", | ||
| "dependencies": { | ||
| "express": "^5.2.1" | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,82 @@ | ||
| import express from "express"; | ||
|
|
||
| const app = express(); | ||
|
|
||
| const PORT = process.env.PORT ? parseInt(process.env.PORT, 10) : 3000; | ||
|
|
||
| const usernameMiddleware = (req, res, next) => { | ||
| const headerValue = req.get("X-username"); | ||
|
|
||
| if (headerValue) { | ||
| req.username = headerValue; | ||
| } else { | ||
| req.username = null; | ||
| } | ||
|
|
||
| next(); | ||
| }; | ||
|
|
||
| const arrayMiddleware = (req, res, next) => { | ||
| const bodyBytes = []; | ||
|
|
||
| // Every time a piece of data arrives, we put it in our list | ||
| req.on("data", (chunk) => { | ||
| bodyBytes.push(...chunk); | ||
| }); | ||
|
|
||
| // When the whole message has arrived, we process it | ||
| req.on("end", () => { | ||
| const bodyString = String.fromCharCode(...bodyBytes); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Lovely manual parser! Since this exercise is all about handling raw bytes, try sending a value with an accent or an emoji — Have a look at what
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Kindly see two screenshots attached of when I used
|
||
|
|
||
| let bodyObject; | ||
| try { | ||
| bodyObject = JSON.parse(bodyString); | ||
| } catch (error) { | ||
| res.status(400).send("Invalid JSON"); | ||
| return; | ||
| } | ||
|
|
||
| if ( | ||
| Array.isArray(bodyObject) && | ||
| bodyObject.every((item) => typeof item === "string") | ||
| ) { | ||
| req.body = bodyObject; | ||
| next(); | ||
| } else { | ||
| res | ||
| .status(400) | ||
| .send( | ||
| "Invalid request body. Expected a JSON array containing only strings.", | ||
| ); | ||
| } | ||
| }); | ||
| }; | ||
|
|
||
| // Hey Express, whenever a POST request arrives at '/', please call this person first (usernameMiddleware), then this person (arrayMiddleware), then finally do the route logic. | ||
| app.post("/", usernameMiddleware, arrayMiddleware, (req, res) => { | ||
| let authPart; | ||
| if (req.username) { | ||
| authPart = `You are authenticated as ${req.username}`; | ||
| } else { | ||
| authPart = "You are not authenticated"; | ||
| } | ||
| const MessageCount = req.body.length; | ||
|
|
||
| const messageJoined = req.body.join(","); | ||
|
|
||
| const word = MessageCount === 1 ? "subject" : "subjects"; | ||
|
|
||
| if (MessageCount > 0) { | ||
| res.send( | ||
| `${authPart}\n\nYou have requested information about ${MessageCount} ${word}: ${messageJoined}.`, | ||
| ); | ||
| } else { | ||
| res.send( | ||
| `${authPart}\n\nYou have requested information about ${MessageCount} ${word}.`, | ||
| ); | ||
| } | ||
| }); | ||
|
|
||
| app.listen(PORT, () => { | ||
| console.log("Type your message here"); | ||
| }); | ||


There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You wrote a really clear, helpful 400 message over in the custom version — "Invalid request body. Expected a JSON array containing only strings." Compare it with
"Error message"here: which one helps the person calling your API understand what went wrong? Could this one tell them as much as the other does?Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The error message in the custom version is more descriptive because it explicitly tells the caller a JSON array of strings is required. This helps them identify exactly which part of their request was invalid, whereas 'Error message' leaves them guessing.
I have updated the error message accordingly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perfect — and your explanation nails exactly why it matters: a descriptive error tells the caller what went wrong so they're not left guessing. 👍 It now matches your nice custom-version message. This is sorted — marking it Complete. (No need to chase the
String.fromCharCode/Bufferquestion for this exercise — that was just an optional rabbit hole if it ever piques your curiosity.) Lovely work, Iswat.