REST Fundamentals
💡 Vad är REST?
REST (REpresentational State Transfer) är som regler för hur API:er ska bete sig på webben.
Tänk dig REST som trafikregler för internet. Precis som alla bilister följer samma regler (rött ljus = stopp, grönt = kör), så följer REST-API:er samma mönster för hur de hanterar data.
🎯 TL;DR
REST = standardiserat sätt att bygga API:er. Använder HTTP-metoder (GET, POST, PUT, DELETE) och logiska URL:er. Enkelt, förutsägbart och alla förstår det.
📋 Efter att ha läst detta kommer du att kunna
- Förstå REST-principerna
- Designa logiska API-endpoints
- Använda rätt HTTP-metoder för olika operationer
- Skapa enhetliga API:er som andra utvecklare förstår
🔧 REST-principerna
Princip 1: Stateless
Varje request innehåller all information som behövs. Servern “kommer inte ihåg” tidigare requests.
Princip 2: Enhetlig interface
Använd samma mönster överallt - om /users/1 ger en användare, så ger /products/5 en produkt.
Princip 3: Cacheable
Svar kan cachas för bättre prestanda.
Princip 4: Resursorienterat
Allt är en “resurs” med en URL - användare, produkter, beställningar, etc.
💭 Tänk så här (REST-mönster)
💬 REST följer ett logiskt mönster:
RESURSER (substantiv):
/users - alla användare
/users/123 - specifik användare
/products - alla produkter
/products/456 - specifik produkt
ÅTGÄRDER (HTTP-verb):
GET - hämta/läsa
POST - skapa
PUT - uppdatera hela resursen
PATCH - uppdatera del av resursen
DELETE - ta bort
🚀 REST i praktiken
🟢 Grundläggande CRUD-operationer
// Typiska REST-endpoints för "users"
[Route("api/[controller]")]
[ApiController]
public class UsersController : ControllerBase
{
// GET /api/users - Hämta alla användare
[HttpGet]
public ActionResult<List<User>> GetAllUsers()
{
return Ok(users);
}
// GET /api/users/5 - Hämta specifik användare
[HttpGet("{id}")]
public ActionResult<User> GetUser(int id)
{
var user = users.FirstOrDefault(u => u.Id == id);
return user == null ? NotFound() : Ok(user);
}
// POST /api/users - Skapa ny användare
[HttpPost]
public ActionResult<User> CreateUser(User user)
{
users.Add(user);
return CreatedAtAction(nameof(GetUser), new { id = user.Id }, user);
}
// PUT /api/users/5 - Uppdatera hela användaren
[HttpPut("{id}")]
public IActionResult UpdateUser(int id, User user)
{
var existingUser = users.FirstOrDefault(u => u.Id == id);
if (existingUser == null) return NotFound();
existingUser.Name = user.Name;
existingUser.Email = user.Email;
return Ok(existingUser);
}
// DELETE /api/users/5 - Ta bort användare
[HttpDelete("{id}")]
public IActionResult DeleteUser(int id)
{
var user = users.FirstOrDefault(u => u.Id == id);
if (user == null) return NotFound();
users.Remove(user);
return NoContent(); // 204
}
}
🟡 Nested resources (kopplade resurser)
// Användares beställningar
[Route("api/users/{userId}/orders")]
[ApiController]
public class UserOrdersController : ControllerBase
{
// GET /api/users/5/orders - Alla beställningar för användare 5
[HttpGet]
public ActionResult<List<Order>> GetUserOrders(int userId)
{
var orders = allOrders.Where(o => o.UserId == userId).ToList();
return Ok(orders);
}
// POST /api/users/5/orders - Skapa beställning för användare 5
[HttpPost]
public ActionResult<Order> CreateUserOrder(int userId, Order order)
{
order.UserId = userId;
allOrders.Add(order);
return CreatedAtAction(nameof(GetUserOrders), new { userId }, order);
}
}
🔴 Avancerat: Filtering och pagination
[HttpGet]
public ActionResult<PagedResult<User>> GetUsers(
[FromQuery] int page = 1,
[FromQuery] int pageSize = 10,
[FromQuery] string? search = null,
[FromQuery] string? sortBy = null)
{
var query = users.AsQueryable();
// Sök
if (!string.IsNullOrEmpty(search))
{
query = query.Where(u => u.Name.Contains(search) ||
u.Email.Contains(search));
}
// Sortera
if (!string.IsNullOrEmpty(sortBy))
{
query = sortBy.ToLower() switch
{
"name" => query.OrderBy(u => u.Name),
"email" => query.OrderBy(u => u.Email),
_ => query.OrderBy(u => u.Id)
};
}
// Paginering
var totalItems = query.Count();
var items = query.Skip((page - 1) * pageSize)
.Take(pageSize)
.ToList();
var result = new PagedResult<User>
{
Items = items,
Page = page,
PageSize = pageSize,
TotalItems = totalItems,
TotalPages = (int)Math.Ceiling(totalItems / (double)pageSize)
};
return Ok(result);
}
public class PagedResult<T>
{
public List<T> Items { get; set; }
public int Page { get; set; }
public int PageSize { get; set; }
public int TotalItems { get; set; }
public int TotalPages { get; set; }
}
📊 HTTP-statuskoder i REST
| Kod | Betydelse | När du använder det |
|---|---|---|
| 200 OK | Allt gick bra | GET, PUT som lyckas |
| 201 Created | Resursen skapades | POST som lyckas |
| 204 No Content | Lyckad, ingen data | DELETE som lyckas |
| 400 Bad Request | Felaktig data skickad | Validation-fel |
| 401 Unauthorized | Inte inloggad | Saknar auth |
| 403 Forbidden | Inte behörig | Fel rättigheter |
| 404 Not Found | Resursen finns inte | Hittar inte ID |
| 500 Server Error | Serverfel | Något gick snett |
⚠️ REST best practices
✅ Bra REST-design
GET /api/users - Hämta alla användare
GET /api/users/123 - Hämta användare 123
POST /api/users - Skapa användare
PUT /api/users/123 - Uppdatera användare 123
DELETE /api/users/123 - Ta bort användare 123
GET /api/users/123/orders - Hämta beställningar för användare 123
❌ Dålig REST-design
GET /api/getUsers - Verb i URL (dåligt)
POST /api/users/123/delete - DELETE-operation via POST
GET /api/user_orders_list - Inkonsekvent namngivning
PUT /api/users/update/123 - Extra "update" i URL
🔧 Vanliga misstag
Misstag 1: Använda verb i URL:er
/api/getUsers❌/api/users✅
Misstag 2: Fel HTTP-metoder
GET /api/users/123/delete❌DELETE /api/users/123✅
Misstag 3: Inkonsekvent namngivning
/api/usersoch/api/Products❌/api/usersoch/api/products✅ (consistent case)
📚 Sammanfattning
REST gör API:er förutsägbara:
- Resurser har logiska URL:er (
/users,/products) - HTTP-metoder beskriver åtgärden (GET, POST, PUT, DELETE)
- Statuskoder berättar vad som hände (200, 404, 500, etc.)
- JSON för dataformat
- Konsistent mönster överallt
🎯 Övningar
- 🟢 Grundläggande: Designa REST-endpoints för en “Books” resurs
- 🟡 Utmanande: Lägg till search och pagination till Books API:et
😄 Obligatorisk dad joke
Varför är REST-API:er så lugna och avslappnade? För att de alltid följer REST och aldrig stressar! 😴