Projektdokumentation "EquipHub" (Fachgespräch)¶
In diesem Dokument ist das Projekt "EquipHub" strikt anhand der Prüfungsfragen und Theorie-Themen des M295 / Fachgesprächs dokumentiert.
1. Client-Server Architektur mit JSON¶
Das Projekt "EquipHub" folgt einer strikten Trennung zwischen Frontend (Client) und Backend (Server). Die gesamte Kommunikation zwischen diesen beiden Schichten erfolgt asynchron über das textbasierte Format JSON (JavaScript Object Notation).
- Im Backend: Die API liefert nach Datenbank-Abfragen reine JSON-Antworten aus, z. B. in routes/equipment.js mit res.json(equipment).
- Im Frontend: Der Client nutzt die Fetch-API, um diese JSON-Daten anzufordern und in verwendbare Objekte umzuwandeln (response.json()), die dann in der Benutzeroberfläche dargestellt werden.
2. Synchroner und asynchroner Code¶
Da Node.js auf einem nicht-blockierenden (asynchronen) I/O-Modell basiert, wurde im Projekt an ressourcenintensiven Stellen asynchron programmiert.
- Ein Beispiel für asynchronen Code im Projekt sind die Datenbankabfragen an MongoDB (await Equipment.find()) in den Routen-Handlern oder das Hashen des Passworts bei der Registrierung (await bcrypt.hash()).
- Durch die Verwendung der Schlüsselwörter async und await pausiert der Codeausführungs-Thread an dieser Stelle "virtuell", blockiert aber nicht den gesamten Server für andere Anfragen.
3. Server Routing und HTTP Methoden¶
Das Routing, also das Zuweisen von URLs zu bestimmten Code-Pfaden, ist über express.Router() sauber gekapselt und modularisiert (routes/equipment.js & routes/auth.js).
Der Server verarbeitet Vorgänge anhand spezifischer HTTP-Methoden (Verben), was dem REST-Prinzip entspricht:
- GET: Listen von Datensätzen abfragen (Bsp: GET /api/equipment um das Inventar auszulesen).
- POST: Neue Datensätze anlegen (Bsp: POST /api/auth/register zur Nutzeranlage oder POST /api/equipment/:id/borrow für Ausleihen).
- PUT: Aktualisieren von bestehenden Ressourcen (Bsp: PUT /api/equipment/:id zum Anpassen der Lagerbestände).
- DELETE: Entfernen von Daten (Bsp: DELETE /api/equipment/:id).
4. HTTP Status Codes¶
Zur sauberen Fehlerbehandlung und Kommunikation mit dem Client (oder Frontend) liefert die API korrekte HTTP Status Codes zurück:
- 200 OK: Erfolgreiche Query (Standard bei GET oder PUT).
- 201 Created: Ressource erfolgreich angelegt (Verwendet bei der register-Route).
- 400 Bad Request: Fehlerhafte Benutzereingaben oder fehlschlagende Logik (z. B. wenn nicht mehr genug Equipment da ist).
- 401 / 403 Forbidden: Fehlendes JWT-Token oder fehlende Administrator-Rollen in der middleware/auth.js.
- 500 Internal Server Error: Serverabstürze (z. B. ein fehlschlagender catch(err) Block beim Datenbankaufruf).
5. Template Engines¶
Das Projekt demonstriert bewusst zwei Paradigmen, um die Template-Vorgabe abzudecken:
- Server-Side Templates (EJS): Über die Route app.get('/liste') wird mithilfe der EJS Template-Engine HTML direkt auf dem Server generiert. Die Daten aus der Datenbank werden im Backend in die Datei views/liste.ejs injiziert und das fertige HTML überliefert.
- Client-Side Rendering (SPA): Die Hauptansicht (z. B. admin.html) ruft JSON von der API ab und baut über JavaScript Template Literals (Backticks) das HTML dynamisch direkt im Browser-DOM auf.
6. API Testing mit Supertest Framework und Postman¶
Um die fehlerfreie Ausführung der Routen dauerhaft sicherzustellen, sind in der Datei tests/app.test.js automatisierte Routentests hinterlegt.
- Es wird das Test-Framework Jest genutzt, kombiniert mit Supertest.
- Supertest simuliert HTTP-Anfragen (wie z. B. auf /api/equipment) an den Express-Server, ohne ihn auf einem physischen Port zu starten, und Jest überprüft mit expect(...) Statements, ob die korrekten Status Codes (200) und JSON-Strukturen zurückkommen.
- Für manuelles Testing und Entwicklungsarbeit wurden die Endpunkte händisch über REST-Clients wie Postman angesprochen.
7. NPM Drittparteienmodule und deren Einbindung¶
Das Rad wird nicht neu erfunden; die Anwendung nutzt via NPM verwaltete Drittparteienmodule (definiert in der package.json):
- express für das HTTP-Hosting und Routing.
- mongoose für die Interaktion mit der Datenbank.
- bcryptjs zum sicheren Hashen der Passwörter bei Login und Registrierung.
- jsonwebtoken zum Ausstellen und Prüfen von sicheren Auth-Tokens.
Diese Module werden mittels der require()-Syntax (z. B. const express = require('express');) in die app.js oder Controller eingebunden.
8. Backend-Applikation vs. API¶
EquipHub ist strategisch als moderne REST-API aufgebaut.
- Eine traditionelle Backend-Applikation verschmilzt oft Logik und Oberfläche (HTML) starr miteinander.
- Da wir jedoch unter z. B. /api/equipment primär reine JSON-Objekte ausliefern, agiert das Backend als reine Daten-Schnittstelle (API). Dies bedeutet, in Zukunft könnte problemlos eine native Handy-App für das Inventar-Management geschrieben werden, die exakt dasselbe Backend (res.json()) ansteuert, ohne die Logik anzupassen. Die Auslieferung von HTML-Dateien aus dem /public Ordner ist physisch abgekapselt.
9. Erstellung CRUD Handlers¶
Die komplette routes/equipment.js ist ein klassischer CRUD-Controller für die Datenbankressourcen:
- Create: Das Generieren von Equipments und das Abspeichern ins Modell (.save()).
- Read: Das Ablesen der Inventardaten (Equipment.find()).
- Update: Dem Array an ausgeliehenen Gütern neue Einträge hinzufügen (borrow/return Routen) oder Namen ändern (findByIdAndUpdate()).
- Delete: Geräte über findByIdAndDelete() restlos aus der Datenbank entfernen.
10. Datenbankanbindung mit NoSQL Datenbank¶
Für die Ausrüstungs- und Benutzerdaten wird MongoDB (Cloud Atlas), eine flexible NoSQL-Lösung, verwendet. Die Anbindung erfolgt über den Node-Treiber Mongoose in app.js.
- Die Datensicherheit ist über Mongoose-Schemas (models/User.js & models/Equipment.js) gewährleistet, die klare Vorgaben über Typisierung (z. B. Strings oder Numbers) treffen.
- Der große NoSQL Vorteil: Im Gegensatz zu relationalen SQL-Datenbanken erlaubt die Dokumentenorientierung von MongoDB die Ausarbeitung von Nested Documents. Im Equipment-Schema wird die gesamte Ausleihhistorie (wer, wann, wie viel verliehen hat) in einem Child-Array (borrows) direkt beim Equipment selbst gespeichert, anstatt zwingend eine zweite Tabellen-Beziehung (JOINs) aufbauen zu müssen. Das macht Leseanfragen enorm effizient.