Dynamische webapplicaties
Wat is een dynamische webapplicatie?¶
Een dynamische webapplicatie is een website die kan veranderen op basis van gebruikersacties en data uit een database. In tegenstelling tot een statische website, waar de inhoud altijd hetzelfde is, kan een dynamische webapplicatie verschillende informatie tonen aan verschillende gebruikers.
Voorbeelden van dynamische webapplicaties: - Een webshop die producten toont uit een database - Een social media platform waar gebruikers berichten kunnen plaatsen - Een vluchten-app die vluchten opzoekt en nieuwe vluchten kan toevoegen
Architectuur van een webapplicatie¶
Een dynamische webapplicatie bestaat uit verschillende onderdelen die samenwerken:
Frontend (Client-side)¶
De frontend is de gebruikersinterface die je ziet in je browser wanneer je een website bezoekt. Het wordt gebouwd met HTML voor de structuur, CSS voor de opmaak en JavaScript voor interactieve functionaliteit. De frontend toont informatie aan de gebruiker, reageert op knoppen en formulieren, en stuurt verzoeken naar de backend wanneer nieuwe gegevens nodig zijn of acties uitgevoerd moeten worden.
Backend (Server-side)¶
De backend is de server die draait op een computer en alle verzoeken van de frontend verwerkt. In onze cursus gebruiken we Node.js als runtime omgeving en Express.js als web framework om de server te bouwen. De backend verwerkt de bedrijfslogica van de applicatie, controleert of gebruikers toegang hebben tot bepaalde functies, communiceert met de database om gegevens op te halen of op te slaan, en stuurt de juiste antwoorden terug naar de frontend.
Database¶
De database is essentieel voor het bewaren van gebruikersgegevens omdat de server zelf geen informatie opslaat - elke keer dat de server opnieuw start, zou alle data verloren gaan zonder een database. De server is "stateless", wat betekent dat hij geen geheugen heeft van eerdere verzoeken of gebruikersacties. Daarom hebben we een aparte database nodig die alle belangrijke informatie permanent opslaat, zoals gebruikersaccounts, productgegevens, vluchten, bestellingen en andere data. Er zijn twee hoofdtypen databases: SQL-databases (zoals MySQL, PostgreSQL, SQLite) die gestructureerde data in tabellen opslaan, en NoSQL-databases (zoals MongoDB) die flexibeler zijn en data kunnen opslaan in verschillende formaten zoals documenten of key-value paren.
API (Application Programming Interface)¶
Een API is de communicatiebrug tussen de frontend en backend, vergelijkbaar met een menu in een restaurant dat laat zien welke gerechten beschikbaar zijn en hoe je ze kunt bestellen. De API definieert welke verzoeken de frontend kan maken naar de backend, welk formaat de gegevens moeten hebben, en hoe de backend zal antwoorden. Dit zorgt ervoor dat frontend en backend op een gestructureerde manier met elkaar kunnen communiceren.
Het 3-lagenmodel¶
In professionele webapplicaties gebruiken we vaak het 3-lagenmodel om de code georganiseerd te houden:
1. API-laag (Presentation Layer)¶
De API-laag is het eerste contactpunt voor alle verzoeken die van de frontend komen. Deze laag definieert alle endpoints (URL's) die beschikbaar zijn en bepaalt welke HTTP-methoden (GET, POST, etc.) gebruikt kunnen worden. De API-laag houdt zich niet bezig met complexe logica, maar fungeert als een doorgeefluik dat verzoeken doorgeeft aan de juiste onderdelen van de applicatie. Het valideert ook of het verzoek het juiste formaat heeft en stuurt passende foutmeldingen terug als er iets misgaat.
// Voorbeeld: Een route die vluchten ophaalt
app.get('/flights', (req, res) => {
// Roept de business-laag aan
businessLayer.getFlights(req.query, (err, flights) => {
if (err) return res.status(500).json({ error: 'Serverfout' });
res.json(flights);
});
});
2. Business-laag (Logic Layer)¶
De business-laag bevat alle bedrijfsregels en logica van de applicatie. Dit is waar beslissingen worden genomen over wat wel en niet toegestaan is. Bijvoorbeeld, mag een gebruiker een vlucht boeken, zijn er nog plaatsen beschikbaar, of heeft de gebruiker de juiste rechten voor een bepaalde actie? Deze laag zorgt ervoor dat alle regels van de applicatie correct worden toegepast voordat er iets gebeurt met de database. De business-laag communiceert met de database-laag om gegevens op te halen of op te slaan, maar houdt zich niet bezig met de technische details van hoe dat gebeurt.
3. Database-laag (Data Layer)¶
De database-laag is verantwoordelijk voor alle communicatie met de database. Deze laag bevat functies die SQL-queries uitvoeren om gegevens op te halen, toe te voegen, te wijzigen of te verwijderen. Door alle database-operaties in een aparte laag te plaatsen, kunnen we makkelijk van database wisselen zonder de rest van de applicatie aan te passen. De database-laag weet niets van bedrijfsregels - het voert simpelweg de opdrachten uit die het krijgt en geeft de resultaten terug.
// Voorbeeld: Functie om alle vluchten op te halen
function getAllFlights(callback) {
const query = 'SELECT * FROM flights';
db.all(query, [], (err, rows) => {
if (err) callback(err);
else callback(null, rows);
});
}
Client-side vs Server-side scripting¶
Client-side scripting (JavaScript in de browser)¶
Client-side scripting draait in de browser van de gebruiker en zorgt voor directe interactie met de webpagina. Deze JavaScript code kan snel reageren op gebruikersacties zoals klikken, typen of hoveren omdat er geen communicatie met de server nodig is voor eenvoudige taken. Het grote voordeel is de snelheid en responsiviteit, maar er zijn ook nadelen: gebruikers kunnen de code bekijken en aanpassen via de ontwikkelaarstools van hun browser, waardoor het niet geschikt is voor gevoelige operaties. Client-side scripting wordt vooral gebruikt voor het valideren van formulieren, het maken van animaties, het tonen en verbergen van elementen, en andere gebruikersinteracties die de ervaring verbeteren.
Server-side scripting (Node.js op de server)¶
Server-side scripting draait op de server en is volledig verborgen voor de gebruiker, wat het veel veiliger maakt voor gevoelige operaties. In onze cursus gebruiken we Node.js voor server-side scripting, wat ons toegang geeft tot de database en andere server-resources. Hoewel server-side operaties langzamer zijn omdat er communicatie tussen browser en server nodig is, zijn ze essentieel voor taken die veiligheid en betrouwbaarheid vereisen. Server-side scripting wordt gebruikt voor database queries, gebruikersauthenticatie, het verwerken van betalingen, bedrijfslogica en alle andere operaties waar de gebruiker geen directe toegang tot mag hebben.
Communicatie tussen frontend en backend¶
De frontend en backend communiceren via HTTP-verzoeken:
- Gebruiker klikt op knop → Frontend JavaScript wordt uitgevoerd
- Frontend stuurt HTTP-verzoek → Naar een API-endpoint op de backend
- Backend verwerkt verzoek → Haalt data op uit database indien nodig
- Backend stuurt antwoord → JSON-data terug naar frontend
- Frontend toont resultaat → Gebruiker ziet de nieuwe informatie
Deze cyclus maakt het mogelijk om dynamische, interactieve webapplicaties te bouwen die real-time kunnen reageren op gebruikersacties.
Praktisch voorbeeld: Luchthavens ophalen¶
Laten we kijken naar een concreet voorbeeld uit de vluchten-app om te zien hoe deze communicatie werkt:
Frontend (HTML + JavaScript):
<button onclick="getAirports()">Toon luchthavens</button>
<table id="airports-table" style="display: none;">
<thead>
<tr><th>Code</th><th>Naam</th><th>Stad</th></tr>
</thead>
<tbody></tbody>
</table>
<script>
async function getAirports() {
try {
const response = await fetch('/airports');
const airports = await response.json();
const tbody = document.querySelector('#airports-table tbody');
tbody.innerHTML = '';
airports.forEach(airport => {
tbody.innerHTML += `<tr>
<td>${airport.code}</td>
<td>${airport.name}</td>
<td>${airport.city}</td>
</tr>`;
});
document.getElementById('airports-table').style.display = 'table';
} catch (error) {
console.error('Fout bij ophalen luchthavens:', error);
}
}
</script>
Backend (Express.js API-endpoint):
app.get('/airports', (req, res) => {
console.log('Verzoek ontvangen voor alle luchthavens');
dblayer.getAllAirports((err, airports) => {
if (err) {
console.error('Database fout:', err);
return res.status(500).json({ error: 'Serverfout' });
}
res.json(airports);
});
});
In dit voorbeeld zie je de complete cyclus: wanneer de gebruiker op de knop klikt, wordt de getAirports()
functie uitgevoerd die een HTTP GET-verzoek stuurt naar /airports
. De backend ontvangt dit verzoek, haalt de luchthavens op uit de database via de database-laag, en stuurt de resultaten terug als JSON. De frontend ontvangt deze data en toont het in een tabel aan de gebruiker.