Einleitung
REST APIs sind das Rueckgrat moderner Web-Applikationen. Jede App die du nutzt – Instagram, Spotify, ChatGPT – kommuniziert ueber REST APIs. In diesem Tutorial baust du eine komplette REST API mit Node.js, Express und TypeScript.
Inhaltsverzeichnis
- Was ist eine REST API?
- Projekt-Setup
- Erste Route erstellen
- CRUD-Operationen
- Middleware
- Eingabe-Validierung
- Datenbank anbinden
- Authentifizierung mit JWT
- Error Handling
- Deployment
1. Was ist eine REST API?
Allerdings gibt es einige wichtige Unterschiede zu beachten.
REST (Representational State Transfer) ist ein Architekturstil fuer Web-APIs. Die Grundprinzipien:
- Ressourcen: Alles ist eine Ressource (User, Produkte, Bestellungen)
- HTTP-Methoden: GET (lesen), POST (erstellen), PUT (aktualisieren), DELETE (loeschen)
- Stateless: Jede Anfrage enthaelt alle noetigen Informationen
- JSON: Standard-Datenformat fuer Request und Response
HTTP-Methoden und CRUD
Grundsätzlich gibt es dabei einige Punkte zu beachten.
| HTTP Methode | CRUD | Beispiel | Beschreibung |
|---|---|---|---|
| GET | Read | GET /api/users | Alle User abrufen |
| GET | Read | GET /api/users/1 | User mit ID 1 abrufen |
| POST | Create | POST /api/users | Neuen User erstellen |
| PUT | Update | PUT /api/users/1 | User 1 aktualisieren |
| DELETE | Delete | DELETE /api/users/1 | User 1 loeschen |
2. Projekt-Setup
Dennoch solltest du einige Besonderheiten beachten.
# Neues Projekt erstellen
mkdir rest-api-tutorial
cd rest-api-tutorial
npm init -y
# Dependencies installieren
npm install express cors dotenv
npm install -D typescript @types/express @types/cors @types/node ts-node nodemon
# TypeScript konfigurieren
npx tsc --init
Dementsprechend ist eine manuelle Überprüfung empfehlenswert.
Projekt-Struktur
Somit kannst du direkt mit der Umsetzung beginnen.
rest-api-tutorial/
├── src/
│ ├── index.ts # Entry Point
│ ├── routes/
│ │ └── users.ts # User Routes
│ ├── middleware/
│ │ ├── auth.ts # Auth Middleware
│ │ └── validate.ts # Validation
│ ├── controllers/
│ │ └── users.ts # User Controller
│ └── types/
│ └── index.ts # TypeScript Types
├── package.json
├── tsconfig.json
└── .env
Folglich erhältst du mit diesem Ansatz deutlich bessere Resultate.
package.json Scripts
Folglich profitierst du von einem besseren Verständnis dieser Konzepte.
{
"scripts": {
"dev": "nodemon --exec ts-node src/index.ts",
"build": "tsc",
"start": "node dist/index.js"
}
}
Ebenfalls sinnvoll ist es, verschiedene Varianten auszuprobieren.
3. Erste Route erstellen
Ebenfalls relevant sind die praktischen Anwendungsbeispiele.
// src/index.ts
import express from 'express';
import cors from 'cors';
import dotenv from 'dotenv';
dotenv.config();
const app = express();
const PORT = process.env.PORT || 3000;
// Middleware
app.use(cors());
app.use(express.json());
// Health Check
app.get('/api/health', (req, res) => {
res.json({
status: 'ok',
timestamp: new Date().toISOString(),
version: '1.0.0'
});
});
// Server starten
app.listen(PORT, () => {
console.log(`🚀 Server laeuft auf http://localhost:${PORT}`);
});
# Starten
npm run dev
# Testen (in neuem Terminal)
curl http://localhost:3000/api/health
Grundsätzlich kannst du diesen Prompt an deine Bedürfnisse anpassen.
4. CRUD-Operationen implementieren
Types definieren
Deshalb lohnt es sich, dieses Thema genauer zu betrachten.
// src/types/index.ts
export interface User {
id: number;
name: string;
email: string;
rolle: 'admin' | 'user';
erstelltAm: Date;
}
export type NeuerUser = Omit<User, 'id' | 'erstelltAm'>;
export type UserUpdate = Partial<NeuerUser>;
Im Grunde funktioniert dieser Ansatz mit allen gängigen AI-Tools.
Controller erstellen
Natürlich gibt es dabei verschiedene Herangehensweisen.
// src/controllers/users.ts
import { Request, Response } from 'express';
import { User, NeuerUser, UserUpdate } from '../types';
// In-Memory Datenbank (spaeter durch echte DB ersetzen)
let users: User[] = [
{ id: 1, name: "Metin", email: "metin@example.de", rolle: "admin", erstelltAm: new Date() },
{ id: 2, name: "Anna", email: "anna@example.de", rolle: "user", erstelltAm: new Date() },
];
let nextId = 3;
// GET /api/users – Alle User abrufen
export const getUsers = (req: Request, res: Response) => {
res.json({
data: users,
total: users.length
});
};
// GET /api/users/:id – Einzelnen User abrufen
export const getUserById = (req: Request, res: Response) => {
const user = users.find(u => u.id === parseInt(req.params.id));
if (!user) {
return res.status(404).json({ error: "User nicht gefunden" });
}
res.json({ data: user });
};
// POST /api/users – Neuen User erstellen
export const createUser = (req: Request, res: Response) => {
const { name, email, rolle }: NeuerUser = req.body;
// Duplikat pruefen
if (users.find(u => u.email === email)) {
return res.status(409).json({ error: "E-Mail existiert bereits" });
}
const neuerUser: User = {
id: nextId++,
name,
email,
rolle: rolle || 'user',
erstelltAm: new Date()
};
users.push(neuerUser);
res.status(201).json({ data: neuerUser });
};
// PUT /api/users/:id – User aktualisieren
export const updateUser = (req: Request, res: Response) => {
const index = users.findIndex(u => u.id === parseInt(req.params.id));
if (index === -1) {
return res.status(404).json({ error: "User nicht gefunden" });
}
const update: UserUpdate = req.body;
users[index] = { ...users[index], ...update };
res.json({ data: users[index] });
};
// DELETE /api/users/:id – User loeschen
export const deleteUser = (req: Request, res: Response) => {
const index = users.findIndex(u => u.id === parseInt(req.params.id));
if (index === -1) {
return res.status(404).json({ error: "User nicht gefunden" });
}
const geloeschterUser = users.splice(index, 1)[0];
res.json({ data: geloeschterUser, message: "User geloescht" });
};
Insbesondere die Struktur des Prompts ist dabei entscheidend für gute Ergebnisse.
Routes definieren
Darüber hinaus bietet dieser Abschnitt konkrete Beispiele und Tipps.
// src/routes/users.ts
import { Router } from 'express';
import {
getUsers, getUserById, createUser,
updateUser, deleteUser
} from '../controllers/users';
const router = Router();
router.get('/', getUsers);
router.get('/:id', getUserById);
router.post('/', createUser);
router.put('/:id', updateUser);
router.delete('/:id', deleteUser);
export default router;
// In src/index.ts hinzufuegen:
import userRoutes from './routes/users';
app.use('/api/users', userRoutes);
Weiterhin ist es ratsam, die Ergebnisse immer kritisch zu prüfen.
5. Middleware
Vor allem für den praktischen Einsatz sind diese Informationen wertvoll.
// src/middleware/logger.ts
import { Request, Response, NextFunction } from 'express';
export const logger = (req: Request, res: Response, next: NextFunction) => {
const start = Date.now();
res.on('finish', () => {
const dauer = Date.now() - start;
console.log(`${req.method} ${req.path} ${res.statusCode} ${dauer}ms`);
});
next();
};
// In index.ts:
app.use(logger);
Darüber hinaus lässt sich das Beispiel leicht erweitern.
6. Eingabe-Validierung
Im Folgenden findest du alle wichtigen Details dazu.
# Zod installieren
npm install zod
Somit sparst du Zeit und erhältst qualitativ hochwertigeren Output.
// src/middleware/validate.ts
import { z } from 'zod';
import { Request, Response, NextFunction } from 'express';
export const userSchema = z.object({
name: z.string().min(2, "Name muss mindestens 2 Zeichen haben"),
email: z.string().email("Ungueltige E-Mail"),
rolle: z.enum(["admin", "user"]).optional()
});
export const validate = (schema: z.ZodSchema) => {
return (req: Request, res: Response, next: NextFunction) => {
const result = schema.safeParse(req.body);
if (!result.success) {
return res.status(400).json({
error: "Validierungsfehler",
details: result.error.errors
});
}
next();
};
};
// In routes/users.ts:
router.post('/', validate(userSchema), createUser);
7. Datenbank anbinden (MongoDB)
Dabei spielen mehrere Faktoren eine wichtige Rolle.
# MongoDB Driver installieren
npm install mongoose
npm install -D @types/mongoose
Natürlich solltest du den generierten Code vor dem Einsatz testen.
// src/models/User.ts
import mongoose from 'mongoose';
const userSchema = new mongoose.Schema({
name: { type: String, required: true, minlength: 2 },
email: { type: String, required: true, unique: true },
passwort: { type: String, required: true },
rolle: { type: String, enum: ['admin', 'user'], default: 'user' }
}, { timestamps: true });
export const UserModel = mongoose.model('User', userSchema);
// Datenbankverbindung in index.ts:
import mongoose from 'mongoose';
mongoose.connect(process.env.MONGODB_URI || 'mongodb://localhost:27017/tutorial')
.then(() => console.log('✅ MongoDB verbunden'))
.catch(err => console.error('❌ MongoDB Fehler:', err));
Vor allem die detaillierten Anweisungen sorgen für präzisere Ergebnisse.
8. Authentifizierung mit JWT
Tatsächlich ist dieser Bereich besonders wichtig für Entwickler.
npm install jsonwebtoken bcryptjs
npm install -D @types/jsonwebtoken @types/bcryptjs
Dabei zeigt dieses Beispiel den grundlegenden Ansatz.
// src/middleware/auth.ts
import jwt from 'jsonwebtoken';
import { Request, Response, NextFunction } from 'express';
const JWT_SECRET = process.env.JWT_SECRET || 'geheim';
// Token generieren
export const generiereToken = (userId: string): string => {
return jwt.sign({ userId }, JWT_SECRET, { expiresIn: '7d' });
};
// Auth Middleware
export const authMiddleware = (req: Request, res: Response, next: NextFunction) => {
const token = req.headers.authorization?.split(' ')[1];
if (!token) {
return res.status(401).json({ error: "Kein Token angegeben" });
}
try {
const decoded = jwt.verify(token, JWT_SECRET) as { userId: string };
(req as any).userId = decoded.userId;
next();
} catch {
res.status(401).json({ error: "Ungueltiger Token" });
}
};
// Geschuetzte Route:
router.get('/profil', authMiddleware, getProfil);
9. Error Handling
Insbesondere für den Einstieg sind die folgenden Informationen hilfreich.
// src/middleware/errorHandler.ts
import { Request, Response, NextFunction } from 'express';
class ApiError extends Error {
statusCode: number;
constructor(statusCode: number, message: string) {
super(message);
this.statusCode = statusCode;
}
}
export const errorHandler = (
err: Error,
req: Request,
res: Response,
next: NextFunction
) => {
if (err instanceof ApiError) {
return res.status(err.statusCode).json({ error: err.message });
}
console.error('Unerwarteter Fehler:', err);
res.status(500).json({ error: 'Interner Serverfehler' });
};
// Nutzung in Controller:
throw new ApiError(404, "User nicht gefunden");
Deshalb empfiehlt es sich, den Prompt schrittweise zu verfeinern.
10. API testen und deployen
Testen mit curl
Im Grunde vereinfacht dieser Ansatz den gesamten Workflow erheblich.
# GET alle User
curl http://localhost:3000/api/users
# POST neuer User
curl -X POST http://localhost:3000/api/users \
-H "Content-Type: application/json" \
-d '{"name":"Peter","email":"peter@test.de"}'
# PUT User aktualisieren
curl -X PUT http://localhost:3000/api/users/1 \
-H "Content-Type: application/json" \
-d '{"name":"Metin Celik"}'
# DELETE User loeschen
curl -X DELETE http://localhost:3000/api/users/2
Außerdem kannst du den Prompt für verschiedene Programmiersprachen anpassen.
Deployment als Docker Container
Ebenso wichtig ist es, die Best Practices zu kennen.
# Dockerfile
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY dist/ ./dist/
EXPOSE 3000
USER node
CMD ["node", "dist/index.js"]
Tatsächlich lässt sich dieser Code direkt in dein Projekt übernehmen.
Naechste Schritte
Zusammenfassend lässt sich sagen, dass dies ein zentraler Aspekt ist.
- API-Design vertiefen: AI fuer REST API Design
- GraphQL lernen: Darüber hinaus a href=“/kuenstliche-intelligenz/ai-fuer-graphql/“>AI fuer GraphQL-Entwicklung
- Testing: Ebenfalls a href=“/kuenstliche-intelligenz/ai-tools-fuer-automatisierte-tests/“>AI Tools fuer automatisierte Tests
- Docker Deployment: Docker Tutorial fuer Containerisierung
- Udemy-Kurse: Vertiefe dein Wissen mit einem JavaScript/Node.js Kurs
Profi-Tipp: Nutze ChatGPT Plus oder GitHub Copilot um API-Endpoints zu generieren. Beschreibe dein Datenmodell und lass die AI den Boilerplate-Code erzeugen – dann passe ihn an deine Beduerfnisse an.