REST Principles ⭐
- Client-Server
- Stateless (no session)
- Cacheable
- Uniform interface
- Layered system
Resource URL Naming
✅ DO:
GET /api/products # collection
GET /api/products/42 # specific
POST /api/products # create
PUT /api/products/42 # full update
PATCH /api/products/42 # partial update
DELETE /api/products/42 # delete
GET /api/products/42/reviews # nested
❌ AVOID:
GET /api/getProducts
GET /api/product/create
POST /api/products/delete/42
GET /api/api/v1/users/getUserById?id=5
Naming Rules
- Plural nouns:
products not product
- kebab-case:
/order-items not /orderItems
- Lowercase:
/users not /Users
- No verbs: action HTTP method-এ থাকে
- Hierarchy:
/users/{id}/orders/{orderId}
Query Parameters
GET /api/products?
search=laptop& # search
category=5& # filter
minPrice=100&maxPrice=1000 # range
sortBy=price&order=desc& # sort
page=2&pageSize=20& # pagination
fields=id,name,price # field selection (sparse fieldsets)
Status Codes ⭐
| Code | কখন |
| 200 OK | সফল GET, PUT, PATCH |
| 201 Created | POST সফল |
| 204 No Content | সফল DELETE |
| 400 Bad Request | Invalid input |
| 401 Unauthorized | Authentication missing/invalid |
| 403 Forbidden | Authenticated but not allowed |
| 404 Not Found | Resource doesn't exist |
| 409 Conflict | Duplicate, version conflict |
| 422 Unprocessable | Validation failed |
| 429 Too Many | Rate limited |
| 500 Server Error | Bug |
Response Format Standard
// Success — single
{
"data": { "id": 1, "name": "Pen" }
}
// Success — list with pagination
{
"data": [...],
"meta": {
"total": 150,
"page": 2,
"pageSize": 20,
"totalPages": 8
},
"links": {
"self": "/api/products?page=2",
"next": "/api/products?page=3",
"prev": "/api/products?page=1"
}
}
// Error
{
"error": {
"code": "VALIDATION_FAILED",
"message": "Invalid input",
"details": [
{ "field": "email", "message": "Invalid format" }
],
"traceId": "abc-123"
}
}
Versioning Strategies
// 1. URL path ⭐
GET /api/v1/products
GET /api/v2/products
// 2. Query string
GET /api/products?api-version=1.0
// 3. Header
GET /api/products
X-API-Version: 2.0
// 4. Media type
Accept: application/vnd.myapi.v2+json
HATEOAS
{
"id": 42, "status": "pending",
"_links": {
"self": { "href": "/orders/42" },
"cancel": { "href": "/orders/42/cancel", "method": "POST" }
}
}
ASP.NET Core Implementation
[ApiController]
[Route("api/v{version:apiVersion}/[controller]")]
[ApiVersion("1.0")]
public class ProductsController : ControllerBase {
[HttpGet]
public async Task<ActionResult<PagedResult<ProductDto>>> GetAll(
[FromQuery] string? search,
[FromQuery] int? categoryId,
[FromQuery] int page = 1,
[FromQuery] int pageSize = 20)
{
var query = _db.Products.AsQueryable();
if (!string.IsNullOrEmpty(search))
query = query.Where(p => p.Name.Contains(search));
if (categoryId.HasValue)
query = query.Where(p => p.CategoryId == categoryId);
var total = await query.CountAsync();
var items = await query
.Skip((page - 1) * pageSize)
.Take(pageSize)
.Select(p => new ProductDto { ... })
.ToListAsync();
return Ok(new PagedResult<ProductDto> {
Data = items,
Meta = new { Total = total, Page = page, PageSize = pageSize }
});
}
}