Tilbake
6.4
Fullstack-applikasjoner

6.4 Fullstack-applikasjoner

Koble frontend og backend med databaser.

65 min
6 oppgaver
FullstackCRUDFrontend-backendDatabase
Du leser den tradisjonelle versjonen
Din fremgang i kapitlet
0 / 6 oppgaver

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.

REST (Representational State Transfer) er ein arkitekturstil for API-ar.

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

✏️Flask REST API
Komplett API for produkt:

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)

Problemet:
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-cors

Enkel CORS (tillat alt - BERRE for utvikling):

from flask import Flask
from flask_cors import CORS

app = Flask(__name__)
CORS(app)  # Tillater alle origins

Trygg 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 response
✏️Fullstack-eksempel: Frontend + Backend
Backend (Flask API - app.py):
from 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!

✏️Feilhandtering i fullstack
Backend - Robuste feilmeldingar:
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), 200

Frontend - 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


BegrepForklaring
FullstackBåde frontend og backend
REST APITeneste 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.