Koble frontend og backend med databaser.
No skal vi kople saman frontend og backend til komplette fullstack-applikasjonar. Vi skal byggje REST API-ar med Flask, kommunisere med dei frå JavaScript, og handtere utfordringar som CORS (Cross-Origin Resource Sharing).
Dette kapittelet gir deg verktøya til å byggje moderne webapplikasjonar der frontend og backend er separate men kommuniserer saumlaust.
Grunnprinsipp:
1. Ressursar blir identifiserte med URL-ar:
- /brukere - samling av brukarar
- /brukere/5 - spesifikk brukar
- /brukere/5/innlegg - innlegg frå brukar 5
2. HTTP-metodar indikerer operasjon:
- GET - Hent data (lesing)
- POST - Opprett ny ressurs
- PUT - Oppdater heile ressursen
- PATCH - Oppdater delar av ressursen
- DELETE - Slett ressurs
3. Statuskodar kommuniserer resultat:
- 200 OK - Vellukka
- 201 Created - Ressurs oppretta
- 400 Bad Request - Ugyldig førespurnad
- 404 Not Found - Ressurs ikkje funnen
- 500 Internal Server Error - Serverfeil
4. Data blir utveksla som JSON
from flask import Flask, jsonify, request
app = Flask(__name__)
# "Database" (i virkeligheten ville dette vært en ekte database)
produkter = [
{"id": 1, "navn": "Laptop", "pris": 8999},
{"id": 2, "navn": "Mus", "pris": 299},
{"id": 3, "navn": "Tastatur", "pris": 599}
]
# GET alle produkter
@app.route('/api/produkter', methods=['GET'])
def hent_alle_produkter():
return jsonify(produkter), 200
# GET ett produkt
@app.route('/api/produkter/<int:id>', methods=['GET'])
def hent_produkt(id):
produkt = next((p for p in produkter if p['id'] == id), None)
if produkt is None:
return jsonify({"error": "Produkt ikke funnet"}), 404
return jsonify(produkt), 200
# POST nytt produkt
@app.route('/api/produkter', methods=['POST'])
def opprett_produkt():
data = request.get_json()
# Validering
if not data or 'navn' not in data or 'pris' not in data:
return jsonify({"error": "Navn og pris er påkrevd"}), 400
# Opprett produkt
nytt_produkt = {
"id": max(p['id'] for p in produkter) + 1,
"navn": data['navn'],
"pris": data['pris']
}
produkter.append(nytt_produkt)
return jsonify(nytt_produkt), 201
# PUT (oppdater produkt)
@app.route('/api/produkter/<int:id>', methods=['PUT'])
def oppdater_produkt(id):
produkt = next((p for p in produkter if p['id'] == id), None)
if produkt is None:
return jsonify({"error": "Produkt ikke funnet"}), 404
data = request.get_json()
produkt['navn'] = data.get('navn', produkt['navn'])
produkt['pris'] = data.get('pris', produkt['pris'])
return jsonify(produkt), 200
# DELETE
@app.route('/api/produkter/<int:id>', methods=['DELETE'])
def slett_produkt(id):
global produkter
produkter = [p for p in produkter if p['id'] != id]
return '', 204 # No Content
if __name__ == '__main__':
app.run(debug=True)Av tryggleiksgrunnar blokkerer nettlesarar førespurnader frå ein origin (domene) til ein annan. Dersom frontend køyrer på
localhost:3000 og backend på localhost:5000, vil førespurnader bli blokkerte.Løysinga: CORS
Backend må eksplisitt tillate førespurnader frå frontend.
Installere Flask-CORS:
pip install flask-corsEnkel CORS (tillat alt - BERRE for utvikling):
from flask import Flask
from flask_cors import CORS
app = Flask(__name__)
CORS(app) # Tillater alle originsTrygg CORS (produksjon):
CORS(app, origins=[
"https://mittdomene.no",
"https://www.mittdomene.no"
])Manuell CORS:
@app.after_request
def after_request(response):
response.headers.add('Access-Control-Allow-Origin', 'http://localhost:3000')
response.headers.add('Access-Control-Allow-Headers', 'Content-Type')
response.headers.add('Access-Control-Allow-Methods', 'GET,POST,PUT,DELETE')
return responsefrom flask import Flask, jsonify, request
from flask_cors import CORS
app = Flask(__name__)
CORS(app)
oppgaver = []
@app.route('/api/oppgaver', methods=['GET'])
def hent_oppgaver():
return jsonify(oppgaver)
@app.route('/api/oppgaver', methods=['POST'])
def legg_til_oppgave():
data = request.get_json()
ny_oppgave = {
'id': len(oppgaver) + 1,
'tekst': data['tekst'],
'fullfort': False
}
oppgaver.append(ny_oppgave)
return jsonify(ny_oppgave), 201
@app.route('/api/oppgaver/<int:id>', methods=['DELETE'])
def slett_oppgave(id):
global oppgaver
oppgaver = [o for o in oppgaver if o['id'] != id]
return '', 204
if __name__ == '__main__':
app.run(debug=True, port=5000)Frontend (HTML + JavaScript - index.html):
<!DOCTYPE html>
<html>
<head>
<title>Oppgaveliste</title>
<style>
body { font-family: Arial; max-width: 600px; margin: 50px auto; }
li { margin: 10px 0; }
button { margin-left: 10px; }
</style>
</head>
<body>
<h1>Oppgaveliste</h1>
<form id="oppgaveSkjema">
<input type="text" id="oppgaveTekst" placeholder="Ny oppgave" required>
<button type="submit">Legg til</button>
</form>
<ul id="oppgaveListe"></ul>
<script>
const API_URL = 'http://localhost:5000/api/oppgaver';
const skjema = document.getElementById('oppgaveSkjema');
const liste = document.getElementById('oppgaveListe');
// Hent og vis oppgaver
async function hentOppgaver() {
const response = await fetch(API_URL);
const oppgaver = await response.json();
liste.innerHTML = '';
oppgaver.forEach(oppgave => {
const li = document.createElement('li');
li.innerHTML = `
${oppgave.tekst}
<button onclick="slettOppgave(${oppgave.id})">Slett</button>
`;
liste.appendChild(li);
});
}
// Legg til oppgave
skjema.addEventListener('submit', async (e) => {
e.preventDefault();
const tekst = document.getElementById('oppgaveTekst').value;
await fetch(API_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ tekst })
});
document.getElementById('oppgaveTekst').value = '';
hentOppgaver();
});
// Slett oppgave
async function slettOppgave(id) {
await fetch(`${API_URL}/${id}`, { method: 'DELETE' });
hentOppgaver();
}
// Last inn oppgaver ved sideinnlasting
hentOppgaver();
</script>
</body>
</html>Dette er ein komplett fullstack-applikasjon med separate frontend og backend!
from flask import Flask, jsonify, request
@app.errorhandler(404)
def not_found(error):
return jsonify({"error": "Ressurs ikke funnet"}), 404
@app.errorhandler(500)
def internal_error(error):
return jsonify({"error": "Intern serverfeil"}), 500
@app.route('/api/bruker/<int:id>', methods=['GET'])
def hent_bruker(id):
bruker = finn_bruker(id) # Fiktiv funksjon
if not bruker:
return jsonify({"error": "Bruker ikke funnet"}), 404
return jsonify(bruker), 200Frontend - Handtere API-feil:
async function hentBruker(id) {
try {
const response = await fetch(`/api/bruker/${id}`);
if (!response.ok) {
// Håndter HTTP-feil (4xx, 5xx)
const error = await response.json();
throw new Error(error.error || 'Noe gikk galt');
}
const bruker = await response.json();
console.log('Bruker:', bruker);
return bruker;
} catch (error) {
console.error('Feil:', error.message);
// Vis feilmelding til bruker
document.getElementById('feilmelding').textContent =
'Kunne ikke hente bruker: ' + error.message;
}
}Beste praksis:
- Backend returnerer klare feilmeldingar som JSON
- Frontend sjekkar response.ok før parsing
- Brukarvennlege feilmeldingar i UI
No skal du løyse oppgåver om fullstack-utvikling, REST API-ar og CORS.
Oppsummering
I dette kapittelet har du lært:
- Fullstack: kombinasjon av frontend og backend.
- Flask REST API: serveren leverer data.
- Frontend + backend: samhandling i ein komplett app.
- Feilhandtering: robust oppførsel i heile stacken.
Nøkkelbegrep
| Begrep | Forklaring |
|---|---|
| Fullstack | Både frontend og backend |
| REST API | Teneste som leverer data over HTTP |
Oppgaver
Dette kapitlet er skrevet av Anthropics toppmodeller (Claude Opus og Claude Fable) og er foreløpig ikke manuelt gjennomgått — kvalitetskontrollen gjøres av uavhengige KI-agenter, og innmeldte feil rettes fortløpende. Funnet en feil? Meld fra, så retter vi den. Les mer om hvordan innholdet lages.