Isaac.

web

Design Stateless APIs

Build scalable APIs without server-side state.

By Emem IsaacJuly 7, 20222 min read
#stateless#scalability#jwt#horizontal scaling
Share:

A Simple Analogy

Stateless APIs are like a grocery clerk with note-taking customers. Customers bring all info they need; clerk doesn't need to remember anyone.


Why Stateless?

  • Scalability: Add servers without coordination
  • Reliability: Server failure doesn't lose state
  • Performance: No state lookup overhead
  • Distribution: Works with load balancers
  • Simplicity: No session management

Token-Based Authentication

// Generate token
public class AuthService
{
    public string GenerateToken(User user)
    {
        var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Jwt:Key"]));
        var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
        
        var claims = new[]
        {
            new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
            new Claim(ClaimTypes.Email, user.Email),
            new Claim(ClaimTypes.Role, user.Role)
        };
        
        var token = new JwtSecurityToken(
            issuer: _config["Jwt:Issuer"],
            audience: _config["Jwt:Audience"],
            claims: claims,
            expires: DateTime.UtcNow.AddHours(24),
            signingCredentials: creds
        );
        
        return new JwtSecurityTokenHandler().WriteToken(token);
    }
}

// Validate token
[Authorize]
[HttpGet("profile")]
public IActionResult GetProfile()
{
    var userId = User.FindFirst(ClaimTypes.NameIdentifier)?.Value;
    return Ok(new { UserId = userId });
}

Request Context

// All info in request/response
[HttpGet("orders/{id}")]
public async Task<IActionResult> GetOrder(int id)
{
    // Get user from token claims
    var userId = int.Parse(User.FindFirst(ClaimTypes.NameIdentifier).Value);
    
    // Query database (no session lookup)
    var order = await _db.Orders
        .FirstOrDefaultAsync(o => o.Id == id && o.UserId == userId);
    
    if (order == null)
        return NotFound();
    
    return Ok(order);
}

Caching Tokens

public class TokenCacheService
{
    private readonly IDistributedCache _cache;
    
    public async Task CacheTokenAsync(string token, string userId, TimeSpan expiration)
    {
        await _cache.SetStringAsync(
            $"token:{token}",
            userId,
            new DistributedCacheEntryOptions { AbsoluteExpirationRelativeToNow = expiration }
        );
    }
    
    public async Task<bool> IsValidTokenAsync(string token)
    {
        var userId = await _cache.GetStringAsync($"token:{token}");
        return userId != null;
    }
    
    public async Task RevokeTokenAsync(string token)
    {
        await _cache.RemoveAsync($"token:{token}");
    }
}

Best Practices

  1. Tokens: Use JWT or similar standards
  2. Short-lived: Access tokens expire quickly
  3. Refresh: Use refresh tokens for long sessions
  4. Immutable: Don't rely on modifying server state
  5. Distributed: Work across multiple servers

Related Concepts

  • JWT tokens
  • OAuth 2.0
  • Load balancing
  • Microservices

Summary

Build stateless APIs using token-based authentication. All user context travels in the token, enabling horizontal scaling.

Share:

Written by Emem Isaac

Expert Software Engineer with 15+ years of experience building scalable enterprise applications. Specialized in ASP.NET Core, Azure, Docker, and modern web development. Passionate about sharing knowledge and helping developers grow.

Ready to Build Something Amazing?

Let's discuss your project and explore how my expertise can help you achieve your goals. Free consultation available.

💼 Trusted by 50+ companies worldwide | ⚡ Average response time: 24 hours