Skip to content
Closed
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .gitignore
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/
60 changes: 60 additions & 0 deletions middleware-exercise/off-the-shelf-middleware.js
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");

Copy link
Copy Markdown

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?

@Iswanna Iswanna Jun 25, 2026

Copy link
Copy Markdown
Author

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.

Copy link
Copy Markdown

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/Buffer question for this exercise — that was just an optional rabbit hole if it ever piques your curiosity.) Lovely work, Iswat.

}
};

// 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");
});
16 changes: 16 additions & 0 deletions middleware-exercise/package.json
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"
}
}
82 changes: 82 additions & 0 deletions middleware-exercise/two-custom-written-middlewares.js
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);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The 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 — ["café"] or ["🐝"] — and see whether it comes back out intact.

Have a look at what String.fromCharCode does with the individual bytes of a multi-byte character, and whether Node gives you a tidier way to turn a set of byte chunks back into a string. (Something to investigate rather than copy: what could you do with the Buffer objects that data hands you?)

@Iswanna Iswanna Jun 25, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

String.fromCharCode will try to turn every single number into its own character. This distorted the output because special characters (like emojis or accents) are actually multi-byte, meaning they need 2 or 4 numbers to stay together to form one character. Buffer.concat keeps those numbers together so .toString() can read them correctly.

Kindly see two screenshots attached of when I used String.fromCharCode and Buffer.concat respectively

Screenshot 2026-06-25 174612 Screenshot 2026-06-25 175030


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");
});