API-Endpunkte (+server.ts)
Wenn du eine echte JSON-API brauchst: +server.ts-Dateien beantworten GET/POST/PUT/DELETE und geben Response-Objekte zurück — für Mobile-Clients, externe Consumer und mehr.
Was ist ein Endpoint?
Eine +server.ts ist eine Route ohne UI. Statt HTML exportiert sie Funktionen, die
nach den HTTP-Methoden benannt sind — GET, POST, PUT, DELETE, PATCH — und jeweils ein Response-Objekt
zurückgeben. Der Helfer json(...) baut daraus bequem eine JSON-Antwort mit
korrektem Content-Type.
// src/routes/api/notes/+server.ts
import { json } from '@sveltejs/kit';
import { db, schema } from '$lib/server/db';
import type { RequestHandler } from './$types';
export const GET: RequestHandler = async ({ locals }) => {
const notes = await db.select().from(schema.notes);
return json(notes); // -> Content-Type: application/json
};🛤️ Rails-Vergleich: +server ≈ API-Controller / respond_to
Eine +server.ts ist dein API-Controller. Wo Rails render json: notes schreibt, schreibst du return json(notes). Die
Methoden-Exports GET/POST/DELETE übernehmen die Rolle
von respond_to bzw. den nach HTTP-Verb gerouteten Controller-Actions.
# Rails: ein API-Controller mit respond_to / render json
class Api::NotesController < ApplicationController
def index
render json: Note.all # ~ return json(notes)
end
def create
note = Note.create!(note_params)
render json: note, status: :created # ~ json(note, { status: 201 })
end
def destroy
Note.find(params[:id]).destroy
head :no_content # ~ new Response(null, { status: 204 })
end
endRequest lesen, Response bauen
In einem Endpoint arbeitest du mit den Web-Standards Request und Response. Einen JSON-Body liest du mit await request.json(), für
Statuscodes und Fehler nutzt du error(status, message) bzw. den zweiten Parameter
von json().
// src/routes/api/notes/+server.ts
import { json, error } from '@sveltejs/kit';
import type { RequestHandler } from './$types';
export const POST: RequestHandler = async ({ request, locals }) => {
if (!locals.user) throw error(401, 'Nicht angemeldet');
const body = await request.json(); // JSON-Body lesen
if (!body.title) throw error(400, 'title fehlt');
const [note] = await db
.insert(schema.notes)
.values({ userId: locals.user.id, title: body.title })
.returning();
return json(note, { status: 201 }); // 201 Created
};// src/routes/api/notes/[id]/+server.ts
import { json } from '@sveltejs/kit';
import type { RequestHandler } from './$types';
export const PUT: RequestHandler = async ({ params, request }) => {
const body = await request.json();
const [note] = await db
.update(schema.notes)
.set({ title: body.title })
.where(eq(schema.notes.id, Number(params.id)))
.returning();
return json(note);
};
export const DELETE: RequestHandler = async ({ params }) => {
await db.delete(schema.notes).where(eq(schema.notes.id, Number(params.id)));
return new Response(null, { status: 204 }); // 204 No Content
};ℹ️ json() vs. new Response()
json(daten, opts) ist die Abkürzung für JSON-Antworten. Für alles andere — 204 No Content, Redirects, Text, Binärdaten — baust du direkt ein new Response(body, { status, headers }).
Endpoint oder Form Action?
Beide schreiben Daten auf dem Server — aber sie zielen auf verschiedene Aufrufer:
- Form Action — für deine eigene Seite. Ein
<form>postet, du bekommst Progressive Enhancement (use:enhance) und automatisches Neuladen der load-Funktionen geschenkt. Das ist fast immer die richtige Wahl innerhalb der App. - +server.ts-Endpoint — wenn der Aufrufer kein Formular deiner Seite ist: eine Mobile-App, ein externer Dienst per Webhook, ein Drittanbieter oder JavaScript, das strukturiertes JSON braucht. Kurz: eine öffentliche/programmatische API.
⚠️ Nicht doppeln
Baue keinen Endpoint nur, um ihn aus dem eigenen Frontend per fetch anzusprechen,
wenn eine Form Action reicht — das kostet dich Progressive Enhancement und die automatische
Invalidierung. Greif zu +server.ts erst, wenn du wirklich eine API für fremde Clients brauchst.
Zusammenfassung
💡 Das Wichtigste
+server.tsexportiertGET/POST/PUT/DELETEund gibtResponse-Objekte zurück.json(daten, { status })für JSON;new Response(null, { status: 204 })für den Rest.request.json()liest den Body;error(status, msg)wirft HTTP-Fehler.- App-intern → Form Action. Externe/mobile/JSON-Clients → Endpoint.
🎯 Probier es selbst
Lege src/routes/api/ping/+server.ts mit einem GET an, das json({ pong: true, time: new Date().toISOString() }) zurückgibt. Rufe die
Route im Browser auf und danach per curl. Ergänze anschließend ein POST, das den JSON-Body zurückspiegelt.