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

graph TB A[Klient] -->|GET /users| B[API] A -->|POST /users| B A -->|PUT /users/123| B A -->|DELETE /users/123| B B --> C{HTTP Method?} C -->|GET| D[Läs användare] C -->|POST| E[Skapa användare] C -->|PUT| F[Uppdatera användare] C -->|DELETE| G[Ta bort användare] D --> H[200 OK + Data] E --> I[201 Created + Data] F --> J[200 OK + Data] G --> K[204 No Content]

🚀 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

KodBetydelseNär du använder det
200 OKAllt gick braGET, PUT som lyckas
201 CreatedResursen skapadesPOST som lyckas
204 No ContentLyckad, ingen dataDELETE som lyckas
400 Bad RequestFelaktig data skickadValidation-fel
401 UnauthorizedInte inloggadSaknar auth
403 ForbiddenInte behörigFel rättigheter
404 Not FoundResursen finns inteHittar inte ID
500 Server ErrorServerfelNå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/users och /api/Products
    • /api/users och /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

  1. 🟢 Grundläggande: Designa REST-endpoints för en “Books” resurs
  2. 🟡 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! 😴


Upp

Upp


Licens: Apache 2.0 | © 2023 Marcus Medina, Campus Mölndal. Alla rättigheter förbehållna.
Du får använda och modifiera detta verk enligt villkoren i Apache License, Version 2.0. Du får inte använda detta verk för kommersiella ändamål utan tillstånd från upphovsmannen.