Vad är REST?
REST (Representational State Transfer) är en arkitekturstil för att designa nätverksapplikationer. En RESTful API använder HTTP-requests för att utföra CRUD-operationer (Create, Read, Update, Delete) på resurser.
REST Principer
1. Client-Server Architecture
Klient och server är separata - de kan utvecklas oberoende.
2. Stateless
Varje request från klient till server måste innehålla all information som behövs. Servern lagrar ingen session state.
3. Cacheable
Responses ska definiera om de kan cachas eller inte.
4. Uniform Interface
Konsekvent interface mellan komponenter.
5. Layered System
Klienten ska inte behöva veta om den pratar direkt med servern eller genom intermediaries.
Resource Naming
Använd Substantiv, Inte Verb
# ✅ Bra
GET /users
GET /users/123
POST /users
PUT /users/123
DELETE /users/123
# ❌ Dåligt
GET /getUsers
POST /createUser
PUT /updateUser/123
DELETE /deleteUser/123
Plural Form
# ✅ Konsekvent plural
/users
/products
/orders
# ❌ Blandning
/user
/products
/order
Hierarkiska Relationer
# ✅ Visa relationer tydligt
GET /users/123/orders
GET /users/123/orders/456
# Läs som: "Get order 456 belonging to user 123"
Använd Kebab-Case
# ✅ Bra
/api/user-profiles
/api/order-items
# ❌ Undvik
/api/user_profiles (snake_case)
/api/userProfiles (camelCase)
HTTP Methods
GET - Hämta Data
GET /api/users
GET /api/users/123
Egenskaper:
- Safe (ändrar inte state)
- Idempotent (samma resultat varje gång)
- Cacheable
POST - Skapa Resurs
POST /api/users
Content-Type: application/json
{
"name": "John Doe",
"email": "john@example.com"
}
Response:
HTTP/1.1 201 Created
Location: /api/users/124
Content-Type: application/json
{
"id": 124,
"name": "John Doe",
"email": "john@example.com",
"createdAt": "2024-12-07T10:00:00Z"
}
PUT - Uppdatera (Ersätt) Resurs
PUT /api/users/123
Content-Type: application/json
{
"name": "John Smith",
"email": "john.smith@example.com"
}
Egenskaper:
- Idempotent
- Ersätter hela resursen
PATCH - Partiell Uppdatering
PATCH /api/users/123
Content-Type: application/json
{
"email": "newemail@example.com"
}
Egenskaper:
- Uppdaterar bara angivna fält
- Inte alltid idempotent
DELETE - Ta bort Resurs
DELETE /api/users/123
Response:
HTTP/1.1 204 No Content
HTTP Status Codes
2xx Success
200 OK - Lyckad GET, PUT, PATCH
201 Created - Lyckad POST
204 No Content - Lyckad DELETE (ingen body)
3xx Redirection
301 Moved Permanently - Resurs flyttad permanent
304 Not Modified - Cachad version är giltig
4xx Client Errors
400 Bad Request - Ogiltig syntax
401 Unauthorized - Authentication krävs
403 Forbidden - Inte behörighet
404 Not Found - Resurs finns inte
405 Method Not Allowed - HTTP method inte tillåten
409 Conflict - Konflikt (t.ex. duplicate)
422 Unprocessable Entity - Validation error
429 Too Many Requests - Rate limit
5xx Server Errors
500 Internal Server Error - Server error
502 Bad Gateway - Upstream error
503 Service Unavailable - Server overloaded
Request & Response Format
Content Negotiation
# Request
GET /api/users/123
Accept: application/json
# Response
HTTP/1.1 200 OK
Content-Type: application/json
{
"id": 123,
"name": "John Doe"
}
Konsekvent JSON Structure
{
"data": {
"id": 123,
"type": "user",
"attributes": {
"name": "John Doe",
"email": "john@example.com"
}
},
"meta": {
"timestamp": "2024-12-07T10:00:00Z"
}
}
Error Responses
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Validation failed",
"details": [
{
"field": "email",
"message": "Invalid email format"
}
]
}
}
Versioning
URL Versioning (Vanligast)
# ✅ Rekommenderat
/api/v1/users
/api/v2/users
Header Versioning
GET /api/users
Accept: application/vnd.myapi.v2+json
Query Parameter (Mindre vanligt)
/api/users?version=2
Best Practice: URL versioning är enklast och tydligast.
Filtering, Sorting & Pagination
Filtering
GET /api/users?role=admin
GET /api/users?status=active&role=admin
GET /api/products?price_min=10&price_max=100
Sorting
GET /api/users?sort=name
GET /api/users?sort=-createdAt # Descending
GET /api/users?sort=name,-age # Multiple fields
Pagination
Offset-based:
GET /api/users?limit=20&offset=40
Cursor-based (Bättre):
GET /api/users?limit=20&cursor=eyJpZCI6MTIzfQ
Response:
{
"data": [...],
"pagination": {
"total": 1000,
"limit": 20,
"offset": 40,
"nextCursor": "eyJpZCI6MTQzfQ",
"hasMore": true
}
}
Field Selection
Låt klienter välja vilka fält de vill ha:
GET /api/users?fields=id,name,email
Response:
{
"data": [
{
"id": 123,
"name": "John Doe",
"email": "john@example.com"
}
]
}
Nästlade Resurser
Shallow Nesting (Rekommenderat)
# ✅ Max 2 nivåer
GET /api/users/123/orders
# ✅ För specifika resurser, använd direkta endpoints
GET /api/orders/456
Deep Nesting (Undvik)
# ❌ För djupt
GET /api/users/123/orders/456/items/789
Authentication & Authorization
Bearer Token (JWT)
GET /api/users/me
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
API Keys
GET /api/users
X-API-Key: your-api-key-here
OAuth 2.0
För third-party access:
1. Authorization request
2. User grants permission
3. Server issues access token
4. Client uses token
Rate Limiting
Headers
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 999
X-RateLimit-Reset: 1701950400
Response när limit nås
HTTP/1.1 429 Too Many Requests
Retry-After: 3600
{
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "Too many requests. Try again in 1 hour."
}
}
HATEOAS
Hypermedia As The Engine Of Application State:
{
"id": 123,
"name": "John Doe",
"links": {
"self": "/api/users/123",
"orders": "/api/users/123/orders",
"profile": "/api/users/123/profile"
}
}
Caching
ETag
# Response
HTTP/1.1 200 OK
ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4"
# Next request
GET /api/users/123
If-None-Match: "33a64df551425fcc55e4d42a148795d9f25f89d4"
# Response if not modified
HTTP/1.1 304 Not Modified
Cache-Control
Cache-Control: max-age=3600, public
Cache-Control: no-cache
Cache-Control: no-store # Känslig data
Idempotency
För säkra retries:
POST /api/orders
Idempotency-Key: a1b2c3d4-e5f6-7890-abcd-ef1234567890
{
"items": [...]
}
Server sparar key och returnerar samma response vid retry.
Security Best Practices
1. HTTPS Only
# Force HTTPS
server {
listen 80;
return 301 https://$host$request_uri;
}
2. Input Validation
// ✅ Validera alla inputs
const schema = z.object({
email: z.string().email(),
age: z.number().min(0).max(150)
});
const result = schema.safeParse(req.body);
if (!result.success) {
return res.status(400).json({
error: result.error
});
}
3. SQL Injection Prevention
// ❌ FARLIGT
db.query(`SELECT * FROM users WHERE id = ${userId}`);
// ✅ Använd prepared statements
db.query('SELECT * FROM users WHERE id = ?', [userId]);
4. Rate Limiting
const rateLimit = require('express-rate-limit');
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100 // 100 requests per window
});
app.use('/api/', limiter);
5. CORS
app.use(cors({
origin: 'https://yourdomain.com',
methods: ['GET', 'POST', 'PUT', 'DELETE'],
allowedHeaders: ['Content-Type', 'Authorization']
}));
Documentation
OpenAPI/Swagger
openapi: 3.0.0
info:
title: My API
version: 1.0.0
paths:
/users:
get:
summary: List users
parameters:
- name: limit
in: query
schema:
type: integer
responses:
'200':
description: Success
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/User'
Testing
Unit Tests
describe('GET /api/users/:id', () => {
it('should return user', async () => {
const res = await request(app)
.get('/api/users/123')
.expect(200);
expect(res.body).toHaveProperty('id', 123);
});
it('should return 404 for non-existent user', async () => {
await request(app)
.get('/api/users/999')
.expect(404);
});
});
Monitoring & Logging
Request Logging
app.use((req, res, next) => {
console.log({
method: req.method,
path: req.path,
query: req.query,
ip: req.ip,
userAgent: req.get('User-Agent')
});
next();
});
Health Check Endpoint
app.get('/health', (req, res) => {
res.json({
status: 'healthy',
timestamp: new Date().toISOString(),
uptime: process.uptime(),
services: {
database: 'connected',
redis: 'connected'
}
});
});
Common Mistakes
1. Verb in URLs
# ❌ Dåligt
POST /api/createUser
GET /api/getUsers
# ✅ Bra
POST /api/users
GET /api/users
2. Wrong Status Codes
// ❌ Dåligt
res.status(200).json({ error: 'User not found' });
// ✅ Bra
res.status(404).json({ error: 'User not found' });
3. Inconsistent Naming
# ❌ Inkonsekvent
/api/users
/api/Products
/api/order_items
# ✅ Konsekvent
/api/users
/api/products
/api/order-items
Performance Optimization
1. Database Queries
// ❌ N+1 problem
const users = await User.findAll();
for (const user of users) {
user.orders = await Order.findAll({ userId: user.id });
}
// ✅ Använd joins
const users = await User.findAll({
include: [Order]
});
2. Response Compression
const compression = require('compression');
app.use(compression());
3. Caching
const redis = require('redis');
const client = redis.createClient();
app.get('/api/users/:id', async (req, res) => {
const cached = await client.get(`user:${req.params.id}`);
if (cached) {
return res.json(JSON.parse(cached));
}
const user = await User.findById(req.params.id);
await client.setex(`user:${req.params.id}`, 3600, JSON.stringify(user));
res.json(user);
});
Verktyg
Testa och bygg APIs:
- API Builder - Bygg och testa API requests
- HTTP Request Builder - Skapa HTTP requests
- HTTP Status Codes - Referens för status codes
Checklista
Design
- RESTful resource naming
- Korrekt användning av HTTP methods
- Korrekt användning av status codes
- Konsekvent error handling
- Versioning strategy
Security
- HTTPS enforced
- Authentication implemented
- Authorization checks
- Input validation
- Rate limiting
- CORS configured
Performance
- Database query optimization
- Caching strategy
- Response compression
- Pagination implemented
Documentation
- API documentation (OpenAPI)
- Example requests/responses
- Error codes documented
- Authentication documented
Monitoring
- Logging implemented
- Health check endpoint
- Error tracking
- Performance monitoring
Slutsats
Viktigaste punkterna:
- Använd substantiv i URLs
- Rätt HTTP methods och status codes
- Konsistent naming och structure
- Robust error handling
- Säkerhet först (HTTPS, validation, rate limiting)
- Dokumentation
- Monitoring och logging
Med dessa best practices bygger du APIs som är:
- Lätta att använda
- Säkra
- Skalbara
- Underhållbara
- Väldokumenterade
Lycka till med din API-design! 🚀