REST APIs bauen
Lerne, wie du mit Velisch in wenigen Zeilen professionelle REST-APIs erstellst. Decorators machen die Entwicklung so einfach wie nie zuvor.
Was du lernen wirst
- HTTP-Methoden mit Decorators definieren (GET, POST, PUT, DELETE)
- URL-Parameter und Query-Parameter verarbeiten
- Eine vollständige CRUD-API implementieren
- Best Practices für API-Design anwenden
Das Decorator-Prinzip
In Velisch definierst du API-Endpunkte mit Decorators. Ein Decorator ist eine spezielle Annotation, die mit @ beginnt und direkt über einer Funktion steht. Er sagt dem Compiler: "Diese Funktion soll als HTTP-Endpunkt erreichbar sein."
Dein erster Endpunkt
Ein einfacher GET-Endpunkt, der einen Text zurückgibt:
@GET("/api/hello")
fn hello(): string {
return "Hello, Velisch!";
}
// Aufruf: GET /api/hello
// Response: "Hello, Velisch!"HTTP-Methoden
@GET– Daten abrufen@POST– Neue Daten erstellen@PUT– Daten aktualisieren@DELETE– Daten löschen@PATCH– Teilaktualisierung
Vorteile des Decorator-Ansatzes
- ✅ Weniger Boilerplate-Code
- ✅ Route und Handler zusammen
- ✅ Automatische JSON-Serialisierung
- ✅ Typsicherheit durchgängig
URL- und Query-Parameter
APIs brauchen oft dynamische Werte – z.B. eine User-ID oder Suchparameter. Velisch unterstützt zwei Arten von Parametern, die direkt als Funktionsargumente ankommen.
URL-Parameter (Path Parameters)
Mit :name definierst du variable Teile in der URL:
@GET("/api/users/:id")
fn getUser(id: string): User {
return db.find(User, id);
}
// Aufruf: GET /api/users/123
// → id = "123"
@GET("/api/posts/:category/:postId")
fn getPost(category: string, postId: string): Post {
return db.findPost(category, postId);
}
// Aufruf: GET /api/posts/tech/456
// → category = "tech", postId = "456"Query-Parameter
Query-Parameter (nach dem ? in der URL) werden automatisch gemappt:
@GET("/api/users")
fn searchUsers(name: string, limit: number): List<User> {
return db.query(User)
.where("name", "LIKE", "%" + name + "%")
.limit(limit)
.findAll();
}
// Aufruf: GET /api/users?name=Anna&limit=10
// → name = "Anna", limit = 10? für optionale Parameter und gib einen Default-Wert an:@GET("/api/users")
fn getUsers(page?: number = 1, limit?: number = 20): List<User> {
return db.findAll(User).paginate(page, limit);
}Request Body & JSON
Bei POST und PUT-Anfragen sendest du Daten im Request Body. Velisch parst JSON automatisch und mappt die Felder auf deine Funktionsparameter oder Structs.
Einfache Parameter
@POST("/api/users")
fn createUser(name: string, email: string): User {
let user = User {
id: generateId(),
name: name,
email: email,
};
return db.save(user);
}
// Request Body:
// { "name": "Anna", "email": "anna@example.com" }
// Response:
// { "id": "usr_abc123", "name": "Anna", "email": "anna@example.com" }Struct als Parameter
Für komplexere Daten nutze einen Struct als Input-Typ:
struct CreateUserInput {
name: string,
email: string,
role: string,
preferences: UserPreferences,
}
struct UserPreferences {
theme: string,
notifications: boolean,
}
@POST("/api/users")
fn createUser(input: CreateUserInput): User {
return db.save(User {
id: generateId(),
name: input.name,
email: input.email,
role: input.role,
preferences: input.preferences,
});
}
// Request Body:
// {
// "name": "Max",
// "email": "max@example.com",
// "role": "admin",
// "preferences": {
// "theme": "dark",
// "notifications": true
// }
// }Vollständige CRUD-API
Jetzt setzen wir alles zusammen: Eine komplette API für User-Management mit Create, Read, Update und Delete. Das ist das Muster, das du für fast jede Ressource in deiner Anwendung nutzen wirst.
// 📦 Datenmodell
struct User {
id: string,
name: string,
email: string,
role: string,
createdAt: string,
}
// 📖 READ - Alle User abrufen
@GET("/api/users")
fn getUsers(): List<User> {
return db.findAll(User);
}
// 📖 READ - Einzelnen User abrufen
@GET("/api/users/:id")
fn getUser(id: string): User {
let user = db.find(User, id);
if (user == null) {
throw HttpError(404, "User nicht gefunden");
}
return user;
}
// ✏️ CREATE - Neuen User erstellen
@POST("/api/users")
fn createUser(name: string, email: string): User {
// Prüfen ob Email bereits existiert
let existing = db.findBy(User, "email", email);
if (existing != null) {
throw HttpError(400, "Email bereits registriert");
}
return db.save(User {
id: generateId(),
name: name,
email: email,
role: "user",
createdAt: now(),
});
}
// 🔄 UPDATE - User aktualisieren
@PUT("/api/users/:id")
fn updateUser(id: string, name: string, email: string): User {
let user = db.find(User, id);
if (user == null) {
throw HttpError(404, "User nicht gefunden");
}
user.name = name;
user.email = email;
return db.save(user);
}
// 🗑️ DELETE - User löschen
@DELETE("/api/users/:id")
fn deleteUser(id: string): void {
let user = db.find(User, id);
if (user == null) {
throw HttpError(404, "User nicht gefunden");
}
db.delete(User, id);
}🛣️ API-Übersicht
| Methode | Route | Beschreibung |
|---|---|---|
| GET | /api/users | Liste aller User |
| GET | /api/users/:id | Einzelner User |
| POST | /api/users | Neuer User erstellen |
| PUT | /api/users/:id | User aktualisieren |
| DELETE | /api/users/:id | User löschen |
Fehlerbehandlung
Gute APIs geben klare Fehlermeldungen. Velisch macht das mit HttpError einfach:
@GET("/api/users/:id")
fn getUser(id: string): User {
let user = db.find(User, id);
// 404 wenn nicht gefunden
if (user == null) {
throw HttpError(404, "User nicht gefunden");
}
// 403 wenn keine Berechtigung
if (!currentUser.canView(user)) {
throw HttpError(403, "Keine Berechtigung");
}
return user;
}
// Response bei Fehler:
// HTTP 404 Not Found
// { "error": "User nicht gefunden", "code": 404 }400Bad Request – Ungültige Eingabe
404Not Found – Ressource existiert nicht
500Server Error – Interner Fehler
Zusammenfassung
Was du gelernt hast:
- HTTP-Decorators: @GET, @POST, @PUT, @DELETE
- URL-Parameter mit
:id - Automatisches JSON-Parsing
- Fehlerbehandlung mit HttpError
Nächste Schritte:
- Lerne Input-Validierung
- Sichere deine APIs mit @Auth
- Verbinde deine API mit einer Datenbank