Node.js
Putting it together
Combine the Quiz class with an Express API.
Combining the quiz with an Express API
The final lesson: we combine the Quiz class with an Express API. The API serves questions as JSON. The previous lessons' Question and Quiz classes are ready; now we add the Express route.
The flow is: set up the Express app, create a Quiz object, add questions, serve them as JSON with a GET /quiz route. With quiz.questions.map(function(q) { return { id: q.id, text: q.text }; }) we take only the id and text properties; we do not send answer because the user should not see it. We send the JSON response with res.json. This is the skeleton of a real API: data is managed in a class, served as JSON over HTTP.
Selecting properties with map
quiz.questions.map(function(q) { return { id: q.id, text: q.text }; }) turns each question into a new object with only id and text. The answer property is removed; the user should not see the correct answer.
map transforms each item of an array and returns a new array. Here we turn each Question object into an { id, text } object. The original array is not changed; only a new view is created. This is the basic way to hide sensitive data in API responses: the data model holds the full object, the API shows only the needed properties. This pattern is also used to strip fields like password or email from a response.
Exercise Add the GET /quiz route
Take the previous lessons' solution (Express and Quiz class are ready). Add the route app.get("/quiz", (req, res) => { res.json(quiz.questions.map(function(q) { return { id: q.id, text: q.text }; })); }). The server requests /quiz.
Show example solution
class Question {
constructor(id, text, answer) {
this.id = id;
this.text = text;
this.answer = answer;
}
}
class Quiz {
constructor() {
this.questions = [];
}
addQuestion(text, answer) {
const id = this.questions.length + 1;
this.questions.push(new Question(id, text, answer));
}
run() {
for (let i = 0; i < this.questions.length; i++) {
console.log("Question " + this.questions[i].id + ": " + this.questions[i].text);
}
}
score(answers) {
let correct = 0;
for (let i = 0; i < this.questions.length; i++) {
if (answers[i] === this.questions[i].answer) {
correct++;
}
}
const total = this.questions.length;
const percentage = Math.round((correct / total) * 100);
return { correct: correct, total: total, percentage: percentage };
}
}
const express = require("express");
const app = express();
app.use(express.json());
const quiz = new Quiz();
quiz.addQuestion("Capital?", "Ankara");
quiz.addQuestion("Largest city?", "Istanbul");
app.get("/quiz", (req, res) => {
res.json(quiz.questions.map(function(q) { return { id: q.id, text: q.text }; }));
});
const server = app.listen(3000, () => {
const http = require("http");
http.get("http://localhost:3000/quiz", (res) => {
let body = "";
res.on("data", (c) => body += c);
res.on("end", () => { console.log("Reply: " + body); server.close(); });
});
});
Test yourself
-
Match each piece to its role
- app.get('/quiz')
- Serves questions as JSON
- quiz.questions.map
- Takes id and text
- res.json
- Sends a JSON response
- express.json()
- Parses the body
-
What does the GET /quiz route return?
app.get("/quiz", (req, res) => res.json(quiz.questions.map(...)))
- All Question objects (including answer)
- Objects containing only id and text
- An empty array
- Skor sonucunu
Show answer
Objects containing only id and text — map takes only id and text; answer is hidden.
-
Complete the map call
res.json(quiz.questions.____(function(q) { return { id: q.id, text: q.text }; }));
Show answer
res.json(quiz.questions.map(function(q) { return { id: q.id, text: q.text }; }));
Next lesson: Async iteration