Aufwärmen · Aufgabe 1 von 7
Aufwärmen aus Lektion I1.3. Was gibt das aus?
async function load() {
return 42;
}
const result = load();
console.log(result instanceof Promise, await result);// I6.1 · ca. 38 Min. · Aufbau
Nach dieser Lektion laden Sie JSON mit fetch von einem Server, unterscheiden eine gescheiterte Anfrage von einem Fehlerstatus und senden JSON mit einer POST-Anfrage.
Lektion 1 von 5 in I6 Netzwerkanfragen und Speicher im Client
Danach können Sie
Aufwärmen · Aufgabe 1 von 7
async function load() {
return 42;
}
const result = load();
console.log(result instanceof Promise, await result);Vorhersagen · Aufgabe 2 von 7
try {
const response = await fetch(api + "/nope");
console.log("fulfilled", response.status, response.ok);
} catch (error) {
console.log("rejected", error.name);
}Üben · Aufgabe 3 von 7
const books = await response.____();Üben · Aufgabe 4 von 7
const response = await fetch(api + "/books");
console.log(response.status, response.headers.get("CONTENT-TYPE"));Üben · Aufgabe 5 von 7
const response = await fetch(api + "/echo", {
method: "POST",
body: {title: "Dune"}
});
console.log(await response.text());Denksport · Aufgabe 6 von 7
const response = await fetch(api + "/books");
console.log((await response.json()).length);
console.log((await response.json()).length);Anwenden · Aufgabe 7 von 7
Prüfen Sie Ihr Ergebnis anhand dieser Liste
Lesen Sie das ausgearbeitete Beispiel und lösen Sie dann die Übungen. Ihr Code läuft in Ihrem Browser oder auf Ihrem Computer und wird nie hochgeladen.
Ausgearbeitetes Beispiel
server.js ist ein kleiner Testserver aus der Standardbibliothek von Node.js: Er lauscht auf einem freien Port Ihres eigenen Computers und kennt /books, /books/1 und /books/2. main.js startet ihn, lädt die Liste, fragt nach einem Buch, das es nicht gibt, sendet per POST ein neues und stoppt zum Schluss den Server und versucht es noch einmal.
main.js
import {startServer} from "./server.js";
const server = await startServer();
const api = server.url; // http://127.0.0.1 and a free port
const response = await fetch(api + "/books");
console.log(response.status, response.ok, response.headers.get("content-type"));
const books = await response.json(); // the body, parsed
console.log(books.map((book) => book.title));
const missing = await fetch(api + "/books/9"); // fulfils: a 404 is a response too
console.log(missing.status, missing.ok, await missing.text());
const created = await fetch(api + "/books", {
method: "POST",
headers: {"Content-Type": "application/json"},
body: JSON.stringify({title: "Kindred", year: 1979})
});
console.log(created.status, await created.json());
await server.close();
try {
await fetch(api + "/books"); // nobody answers any more
} catch (error) {
console.log(error.name + ": " + error.message);
}
server.js
// A small test server for this lesson, made with node:http. You do not need to change it.
import {createServer} from "node:http";
export async function startServer() {
const books = [
{id: 1, title: "Dune", year: 1965},
{id: 2, title: "Emma", year: 1815}
];
const server = createServer(async (request, response) => {
let body = "";
for await (const chunk of request) body += chunk;
const send = (status, type, text) => {
response.writeHead(status, {"Content-Type": type});
response.end(text);
};
const id = Number(request.url.split("/")[2]); // "/books/2" gives 2
if (request.method === "GET" && request.url === "/books") {
send(200, "application/json", JSON.stringify(books));
} else if (request.method === "GET" && request.url.startsWith("/books/")) {
const book = books.find((b) => b.id === id);
if (book) send(200, "application/json", JSON.stringify(book));
else send(404, "text/plain", "Not found");
} else if (request.method === "POST" && request.url === "/books") {
if (request.headers["content-type"] !== "application/json") {
return send(415, "application/json", JSON.stringify({error: "Send JSON"}));
}
try {
const book = {id: books.length + 1, ...JSON.parse(body)};
books.push(book);
send(201, "application/json", JSON.stringify(book));
} catch {
send(400, "application/json", JSON.stringify({error: "Bad JSON"}));
}
} else if (request.method === "POST" && request.url === "/echo") {
send(200, "text/plain", body); // sends back exactly what it got
} else {
send(404, "text/plain", "Not found");
}
});
await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve)); // port 0: any free port
return {
url: `http://127.0.0.1:${server.address().port}`,
close: () => new Promise((resolve) => server.close(resolve))
};
}
Ausführen mit
node main.jsAusgabe
200 true application/json
[ 'Dune', 'Emma' ]
404 false Not found
201 { id: 3, title: 'Kindred', year: 1979 }
TypeError: fetch failedÜbung 1 von 3
getTitles(baseUrl) soll baseUrl + "/books" laden, ein JSON-Array mit Büchern, und ein Array ihrer Titel zurückgeben. Im Starter fehlen zwei awaits, daher ruft er json() auf einem Promise statt auf einer Response auf.
Diese Übung braucht Node.js auf Ihrem Computer (die Browser-Version kann sie nicht ausführen). Dateien und Befehle stehen unten.
Führen Sie die Tests aus: response.json is not a function. Was ist response, bevor Sie darauf warten?
fetch gibt ein Promise einer Response zurück, und json() gibt ein Promise der Daten zurück.
const response = await fetch(…); const books = await response.json();
Ein möglicher Lösungsweg. Ihrer kann anders aussehen und trotzdem alle Prüfungen bestehen.
export async function getTitles(baseUrl) {
const response = await fetch(baseUrl + "/books");
const books = await response.json();
return books.map((book) => book.title);
}
Installieren Sie ECMAScript 2026 oder neuer. Speichern Sie diese Dateien in einem Ordner, öffnen Sie dort ein Terminal und führen Sie die Befehle unten aus.
main.js
export async function getTitles(baseUrl) {
const response = fetch(baseUrl + "/books");
const books = response.json();
return books.map((book) => book.title);
}
main.test.js
import {test} from 'node:test';
import assert from 'node:assert/strict';
import {startServer} from './server.js';
import {getTitles} from './main.js';
test('getTitles gives the titles of both books', async () => {
const server = await startServer();
try {
const titles = await getTitles(server.url);
assert.deepEqual(titles, ['Dune', 'Emma'], `getTitles gave ${JSON.stringify(titles)}`);
} finally {
await server.close();
}
});
server.js
// A small test server for this lesson, made with node:http. You do not need to change it.
import {createServer} from "node:http";
export async function startServer() {
const books = [
{id: 1, title: "Dune", year: 1965},
{id: 2, title: "Emma", year: 1815}
];
const server = createServer(async (request, response) => {
let body = "";
for await (const chunk of request) body += chunk;
const send = (status, type, text) => {
response.writeHead(status, {"Content-Type": type});
response.end(text);
};
const id = Number(request.url.split("/")[2]); // "/books/2" gives 2
if (request.method === "GET" && request.url === "/books") {
send(200, "application/json", JSON.stringify(books));
} else if (request.method === "GET" && request.url.startsWith("/books/")) {
const book = books.find((b) => b.id === id);
if (book) send(200, "application/json", JSON.stringify(book));
else send(404, "text/plain", "Not found");
} else if (request.method === "POST" && request.url === "/books") {
if (request.headers["content-type"] !== "application/json") {
return send(415, "application/json", JSON.stringify({error: "Send JSON"}));
}
try {
const book = {id: books.length + 1, ...JSON.parse(body)};
books.push(book);
send(201, "application/json", JSON.stringify(book));
} catch {
send(400, "application/json", JSON.stringify({error: "Bad JSON"}));
}
} else if (request.method === "POST" && request.url === "/echo") {
send(200, "text/plain", body); // sends back exactly what it got
} else {
send(404, "text/plain", "Not found");
}
});
await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve)); // port 0: any free port
return {
url: `http://127.0.0.1:${server.address().port}`,
close: () => new Promise((resolve) => server.close(resolve))
};
}
package.json
{
"type": "module"
}
package.json sagt Node.js, dass die .js-Dateien Module sind; lassen Sie die Datei im Ordner.
Programm ausführen:
node main.jsPrüfungen ausführen (learnrun.js muss im selben Ordner liegen):
node --testlearnrun.js herunterladenÜbung 2 von 3
findBook(baseUrl, id) lädt baseUrl + "/books/" + id und gibt das Buch zurück. Für ein Buch, das es nicht gibt, antwortet der Server mit 404 und dem Text Not found, und findBook muss null zurückgeben. Der Starter parst jede Antwort als JSON, daher wirft ein fehlendes Buch einen SyntaxError. Prüfen Sie zuerst den Status.
Diese Übung braucht Node.js auf Ihrem Computer (die Browser-Version kann sie nicht ausführen). Dateien und Befehle stehen unten.
Führen Sie die Tests aus: Buch 9 gibt einen SyntaxError. Was hat der Server geschickt, und wurde fetch abgelehnt?
Ein 404 erfüllt das fetch-Promise trotzdem. response.status nennt Ihnen den Code.
if (response.status === 404) return null; bevor Sie json() aufrufen.
Ein möglicher Lösungsweg. Ihrer kann anders aussehen und trotzdem alle Prüfungen bestehen.
export async function findBook(baseUrl, id) {
const response = await fetch(baseUrl + "/books/" + id);
if (response.status === 404) return null;
return response.json();
}
Installieren Sie ECMAScript 2026 oder neuer. Speichern Sie diese Dateien in einem Ordner, öffnen Sie dort ein Terminal und führen Sie die Befehle unten aus.
main.js
export async function findBook(baseUrl, id) {
const response = await fetch(baseUrl + "/books/" + id);
return response.json();
}
main.test.js
import {test} from 'node:test';
import assert from 'node:assert/strict';
import {startServer} from './server.js';
import {findBook} from './main.js';
test('findBook gives book 2', async () => {
const server = await startServer();
try {
const book = await findBook(server.url, 2);
assert.deepEqual(book, {id: 2, title: 'Emma', year: 1815}, `findBook gave ${JSON.stringify(book)}`);
} finally {
await server.close();
}
});
test('findBook gives null for a book that does not exist', async () => {
const server = await startServer();
try {
const book = await findBook(server.url, 9);
assert.equal(book, null, `findBook gave ${JSON.stringify(book)}`);
} finally {
await server.close();
}
});
server.js
// A small test server for this lesson, made with node:http. You do not need to change it.
import {createServer} from "node:http";
export async function startServer() {
const books = [
{id: 1, title: "Dune", year: 1965},
{id: 2, title: "Emma", year: 1815}
];
const server = createServer(async (request, response) => {
let body = "";
for await (const chunk of request) body += chunk;
const send = (status, type, text) => {
response.writeHead(status, {"Content-Type": type});
response.end(text);
};
const id = Number(request.url.split("/")[2]); // "/books/2" gives 2
if (request.method === "GET" && request.url === "/books") {
send(200, "application/json", JSON.stringify(books));
} else if (request.method === "GET" && request.url.startsWith("/books/")) {
const book = books.find((b) => b.id === id);
if (book) send(200, "application/json", JSON.stringify(book));
else send(404, "text/plain", "Not found");
} else if (request.method === "POST" && request.url === "/books") {
if (request.headers["content-type"] !== "application/json") {
return send(415, "application/json", JSON.stringify({error: "Send JSON"}));
}
try {
const book = {id: books.length + 1, ...JSON.parse(body)};
books.push(book);
send(201, "application/json", JSON.stringify(book));
} catch {
send(400, "application/json", JSON.stringify({error: "Bad JSON"}));
}
} else if (request.method === "POST" && request.url === "/echo") {
send(200, "text/plain", body); // sends back exactly what it got
} else {
send(404, "text/plain", "Not found");
}
});
await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve)); // port 0: any free port
return {
url: `http://127.0.0.1:${server.address().port}`,
close: () => new Promise((resolve) => server.close(resolve))
};
}
package.json
{
"type": "module"
}
package.json sagt Node.js, dass die .js-Dateien Module sind; lassen Sie die Datei im Ordner.
Programm ausführen:
node main.jsPrüfungen ausführen (learnrun.js muss im selben Ordner liegen):
node --testlearnrun.js herunterladenÜbung 3 von 3
addBook(baseUrl, book) soll das Buch als JSON per POST an baseUrl + "/books" schicken und zurückgeben, was der Server zurücksendet: das Buch mit seiner neuen id. Der Server nimmt nur einen Body mit dem Header Content-Type: application/json an. Der Starter sendet das Objekt selbst, ohne den Header, und bekommt ein Fehlerobjekt zurück.
Diese Übung braucht Node.js auf Ihrem Computer (die Browser-Version kann sie nicht ausführen). Dateien und Befehle stehen unten.
Führen Sie die Tests aus: addBook gibt {"error":"Send JSON"} zurück. Was will der Server sehen?
Zwei Dinge fehlen: eine headers-Option und ein Body, der ein JSON-String ist.
headers: {"Content-Type": "application/json"}, body: JSON.stringify(book)
Ein möglicher Lösungsweg. Ihrer kann anders aussehen und trotzdem alle Prüfungen bestehen.
export async function addBook(baseUrl, book) {
const response = await fetch(baseUrl + "/books", {
method: "POST",
headers: {"Content-Type": "application/json"},
body: JSON.stringify(book)
});
return response.json();
}
Installieren Sie ECMAScript 2026 oder neuer. Speichern Sie diese Dateien in einem Ordner, öffnen Sie dort ein Terminal und führen Sie die Befehle unten aus.
main.js
export async function addBook(baseUrl, book) {
const response = await fetch(baseUrl + "/books", {
method: "POST",
body: book
});
return response.json();
}
main.test.js
import {test} from 'node:test';
import assert from 'node:assert/strict';
import {startServer} from './server.js';
import {addBook} from './main.js';
test('addBook gives the new book with id 3', async () => {
const server = await startServer();
try {
const added = await addBook(server.url, {title: 'Kindred', year: 1979});
assert.deepEqual(added, {id: 3, title: 'Kindred', year: 1979}, `addBook gave ${JSON.stringify(added)}`);
} finally {
await server.close();
}
});
test('After addBook the server has three books', async () => {
const server = await startServer();
try {
await addBook(server.url, {title: 'Kindred', year: 1979});
const books = await (await fetch(server.url + '/books')).json();
assert.equal(books.length, 3, `the server has ${books.length} books`);
} finally {
await server.close();
}
});
server.js
// A small test server for this lesson, made with node:http. You do not need to change it.
import {createServer} from "node:http";
export async function startServer() {
const books = [
{id: 1, title: "Dune", year: 1965},
{id: 2, title: "Emma", year: 1815}
];
const server = createServer(async (request, response) => {
let body = "";
for await (const chunk of request) body += chunk;
const send = (status, type, text) => {
response.writeHead(status, {"Content-Type": type});
response.end(text);
};
const id = Number(request.url.split("/")[2]); // "/books/2" gives 2
if (request.method === "GET" && request.url === "/books") {
send(200, "application/json", JSON.stringify(books));
} else if (request.method === "GET" && request.url.startsWith("/books/")) {
const book = books.find((b) => b.id === id);
if (book) send(200, "application/json", JSON.stringify(book));
else send(404, "text/plain", "Not found");
} else if (request.method === "POST" && request.url === "/books") {
if (request.headers["content-type"] !== "application/json") {
return send(415, "application/json", JSON.stringify({error: "Send JSON"}));
}
try {
const book = {id: books.length + 1, ...JSON.parse(body)};
books.push(book);
send(201, "application/json", JSON.stringify(book));
} catch {
send(400, "application/json", JSON.stringify({error: "Bad JSON"}));
}
} else if (request.method === "POST" && request.url === "/echo") {
send(200, "text/plain", body); // sends back exactly what it got
} else {
send(404, "text/plain", "Not found");
}
});
await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve)); // port 0: any free port
return {
url: `http://127.0.0.1:${server.address().port}`,
close: () => new Promise((resolve) => server.close(resolve))
};
}
package.json
{
"type": "module"
}
package.json sagt Node.js, dass die .js-Dateien Module sind; lassen Sie die Datei im Ordner.
Programm ausführen:
node main.jsPrüfungen ausführen (learnrun.js muss im selben Ordner liegen):
node --testlearnrun.js herunterladenimport {startServer} from "./server.js";
const server = await startServer();
try {
const response = await fetch(server.url + "/books");
console.log("Books:", (await response.json()).length);
const books = await response.json();
console.log(books[0].title);
} finally {
await server.close();
}
Was Node.js ausgibt
TypeError: Body is unusable: Body has already been readWarum, und die Lösung
Der Body ist ein Stream, und json() liest ihn bis zum Ende. Ein zweites json(), text() oder jede andere Body-Methode hat nichts mehr zu lesen. Lesen Sie den Body einmal, behalten Sie das Ergebnis in einer Variablen und verwenden Sie diese: const books = await response.json(); console.log("Books:", books.length);.
import {startServer} from "./server.js";
const server = await startServer();
try {
const response = fetch(server.url + "/books");
const books = await response.json();
console.log(books.length);
} finally {
await server.close();
}
Was Node.js ausgibt
TypeError: response.json is not a functionWarum, und die Lösung
fetch gibt ein Promise zurück, nicht die Response. Ein Promise hat then und catch, aber keine Methode json. Warten Sie zuerst auf fetch: const response = await fetch(…);, und warten Sie dann auch auf den Body.
import {startServer} from "./server.js";
const server = await startServer();
try {
const response = await fetch(server.url + "/books/9");
const book = await response.json();
console.log(book.title);
} finally {
await server.close();
}
Was Node.js ausgibt
SyntaxError: Unexpected token 'N', "Not found" is not valid JSONWarum, und die Lösung
Es gibt kein Buch 9, daher antwortete der Server mit 404 und dem Text Not found. fetch wurde nicht abgelehnt, denn ein 404 ist eine Antwort. json() versuchte dann, diesen Text zu parsen. Prüfen Sie zuerst die Response: if (!response.ok) { … }, bevor Sie den Body als JSON lesen.
JavaScript im Browser: die eigene Engine Ihres Browsers in einem abgeschotteten Worker. Syntaxfehler findet acorn 8.18.0, MIT. Lizenz und Quellcode
5 Fragen, ohne Hinweise. Ab 80 % ist die Lektion abgeschlossen.
Erledigen Sie zuerst alle Aufgaben oben, um das Abschlussquiz freizuschalten.