REST va API dizayni

30.06.2026 | Muallif: Jaxongir a.k.a | Kategoriya: Microservice Architecture | 33 daqiqa o'qish

REST & API Design - backend’da endpoint yozish emas, balki API’ni tushunarli, barqaror, kengayadigan, xavfsiz va clientlar uchun qulay qilib loyihalash.

Bu bo‘limga quyidagilar kiradi: RESTful conventions, HATEOAS, OpenAPI/Swagger, RFC 7807 error response, versioning strategies, HTTP caching headers.


1. API Design nima?

API Design - frontend, mobile app yoki boshqa backend service sening backend’ing bilan qanday gaplashishini loyihalash.

Masalan:

Mobile app → Backend API → Database
Frontend → Backend API → Payment service
CRM → Backend API → Order service

Yaxshi API:

tushunarli
consistent
xatolarni aniq qaytaradi
version bilan boshqariladi
pagination/filter/sort bor
security hisobga olingan
documentation bor

Yomon API esa ishlaydi, lekin vaqt o‘tib muammo qiladi.


2. REST nima?

REST - HTTP asosida resource’lar bilan ishlash uslubi.

REST’da asosiy narsa - resource.

Resource misollar:

users
orders
products
payments
categories

Endpoint resource nomi bilan yoziladi.

Yaxshi:

GET /api/users
GET /api/users/10
POST /api/users
PUT /api/users/10
DELETE /api/users/10

Yomon:

GET /api/getUsers
GET /api/getUserById?id=10
POST /api/createUser
POST /api/deleteUser
POST /api/updateUser

REST’da action’ni URL emas, HTTP method bildiradi.


3. HTTP Methods

Method

Vazifasi

Misol

GET

Ma’lumot olish

GET /users/1

POST

Yangi resource yaratish

POST /users

PUT

Resource’ni to‘liq yangilash

PUT /users/1

PATCH

Qisman yangilash

PATCH /users/1/status

DELETE

O‘chirish

DELETE /users/1


4. Resource nomlash

Yaxshi qoida

Resource nomlari odatda plural bo‘ladi:

/users
/orders
/products
/payments

Yomon:

/user
/order
/product

Sabab: /users userlar collection’ini bildiradi.


Nested resource

Agar orderlar userga tegishli bo‘lsa:

GET /api/users/10/orders

Bu 10-idli userning orderlarini olish degani.

Lekin haddan tashqari chuqur nesting yomon:

GET /api/users/10/orders/5/items/3/products/8

Yaxshiroq:

GET /api/order-items/3
GET /api/products/8

Odatda 2 darajadan oshirmaslik yaxshi.


5. HTTP Status Codes

Status code API’da juda muhim. Client nima bo‘lganini statusdan tushunishi kerak.

Status

Ma’nosi

200 OK

Muvaffaqiyatli

201 Created

Yangi resource yaratildi

204 No Content

Muvaffaqiyatli, response body yo‘q

400 Bad Request

Request noto‘g‘ri

401 Unauthorized

Login/token yo‘q yoki noto‘g‘ri

403 Forbidden

Ruxsat yo‘q

404 Not Found

Resource topilmadi

409 Conflict

Conflict holat

422 Unprocessable Entity

Validation/business rule xato

500 Internal Server Error

Server xatosi


6. 200, 201, 204 farqi

200 OK

Ma’lumot qaytarilganda:

GET /api/users/1

Response:

200 OK
{
  "id": 1,
  "name": "Ali"
}

201 Created

Yangi resource yaratilganda:

POST /api/users

Response:

201 Created
Location: /api/users/1
{
  "id": 1,
  "name": "Ali"
}

Spring’da:

@PostMapping
public ResponseEntity<UserDto> create(@Valid @RequestBody CreateUserRequest request) {
    UserDto user = userService.create(request);

    return ResponseEntity
            .created(URI.create("/api/users/" + user.id()))
            .body(user);
}

204 No Content

Delete yoki response body kerak bo‘lmagan update uchun:

@DeleteMapping("/{id}")
public ResponseEntity<Void> delete(@PathVariable Long id) {
    userService.delete(id);
    return ResponseEntity.noContent().build();
}

Response:

204 No Content

Body bo‘lmaydi.


7. RESTful Controller misol

@RestController
@RequestMapping("/api/users")
public class UserController {

    private final UserService userService;

    public UserController(UserService userService) {
        this.userService = userService;
    }

    @GetMapping
    public Page<UserDto> getUsers(Pageable pageable) {
        return userService.getUsers(pageable);
    }

    @GetMapping("/{id}")
    public UserDto getById(@PathVariable Long id) {
        return userService.getById(id);
    }

    @PostMapping
    public ResponseEntity<UserDto> create(@Valid @RequestBody CreateUserRequest request) {
        UserDto created = userService.create(request);

        return ResponseEntity
                .created(URI.create("/api/users/" + created.id()))
                .body(created);
    }

    @PutMapping("/{id}")
    public UserDto update(
            @PathVariable Long id,
            @Valid @RequestBody UpdateUserRequest request
    ) {
        return userService.update(id, request);
    }

    @DeleteMapping("/{id}")
    public ResponseEntity<Void> delete(@PathVariable Long id) {
        userService.delete(id);
        return ResponseEntity.noContent().build();
    }
}

8. Request DTO va Response DTO

Entity’ni API’da to‘g‘ridan-to‘g‘ri qaytarish yomon.

Yomon:

@GetMapping("/{id}")
public User getUser(@PathVariable Long id) {
    return userRepository.findById(id).orElseThrow();
}

Muammolar:

sensitive field chiqib ketishi mumkin
LazyInitializationException bo‘lishi mumkin
JSON recursion bo‘lishi mumkin
API entity structure’ga bog‘lanib qoladi

Yaxshi:

public record UserDto(
        Long id,
        String name,
        String email
) {
    public static UserDto from(User user) {
        return new UserDto(
                user.getId(),
                user.getName(),
                user.getEmail()
        );
    }
}

Request DTO:

public record CreateUserRequest(
        @NotBlank String name,
        @Email String email,
        @Pattern(regexp = "^\\+998\\d{9}$") String phone
) {
}

9. Validation

API request kelganda darhol validation qilish kerak.

@PostMapping
public ResponseEntity<UserDto> create(@Valid @RequestBody CreateUserRequest request) {
    UserDto user = userService.create(request);
    return ResponseEntity.status(HttpStatus.CREATED).body(user);
}

DTO:

public record CreateUserRequest(
        @NotBlank(message = "Ism bo‘sh bo‘lmasin")
        String name,

        @Email(message = "Email noto‘g‘ri formatda")
        String email,

        @Pattern(regexp = "^\\+998\\d{9}$", message = "Telefon +998 bilan boshlanishi kerak")
        String phone
) {
}

10. Error Response

Yomon error response:

{
  "error": "Something went wrong"
}

Client buni tushunmaydi.

Yaxshi error response:

{
  "type": "https://api.example.com/errors/validation",
  "title": "Validation failed",
  "status": 400,
  "detail": "Request body has invalid fields",
  "instance": "/api/users",
  "errors": [
    {
      "field": "email",
      "message": "Email noto‘g‘ri formatda"
    }
  ]
}

Bu RFC 7807 uslubiga yaqin.


11. RFC 7807 - Problem Details

RFC 7807 - HTTP API xatolarini standart ko‘rinishda qaytarish formati.

Asosiy fieldlar:

Field

Ma’nosi

type

Error turi haqida URI

title

Qisqa error nomi

status

HTTP status code

detail

Batafsil xabar

instance

Qaysi endpointda bo‘ldi

Spring 6 / Spring Boot 3'da ProblemDetail bor.

@ExceptionHandler(UserNotFoundException.class)
public ResponseEntity<ProblemDetail> handleUserNotFound(UserNotFoundException ex) {
    ProblemDetail problem = ProblemDetail.forStatus(HttpStatus.NOT_FOUND);

    problem.setTitle("User not found");
    problem.setDetail(ex.getMessage());
    problem.setType(URI.create("https://api.example.com/errors/user-not-found"));

    return ResponseEntity.status(HttpStatus.NOT_FOUND).body(problem);
}

Response:

{
  "type": "https://api.example.com/errors/user-not-found",
  "title": "User not found",
  "status": 404,
  "detail": "User not found with id: 10"
}

12. Global Exception Handler

API’da xatolarni bitta joyda boshqarish kerak.

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(UserNotFoundException.class)
    public ResponseEntity<ProblemDetail> handleUserNotFound(UserNotFoundException ex) {
        ProblemDetail problem = ProblemDetail.forStatus(HttpStatus.NOT_FOUND);
        problem.setTitle("User not found");
        problem.setDetail(ex.getMessage());
        problem.setType(URI.create("https://api.example.com/errors/user-not-found"));

        return ResponseEntity.status(HttpStatus.NOT_FOUND).body(problem);
    }

    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<ValidationProblemDetail> handleValidation(
            MethodArgumentNotValidException ex,
            HttpServletRequest request
    ) {
        List<FieldErrorDto> errors = ex.getBindingResult()
                .getFieldErrors()
                .stream()
                .map(error -> new FieldErrorDto(
                        error.getField(),
                        error.getDefaultMessage()
                ))
                .toList();

        ValidationProblemDetail body = new ValidationProblemDetail(
                "https://api.example.com/errors/validation",
                "Validation failed",
                400,
                "Request body has invalid fields",
                request.getRequestURI(),
                errors
        );

        return ResponseEntity.badRequest().body(body);
    }
}

DTO:

public record FieldErrorDto(
        String field,
        String message
) {
}
public record ValidationProblemDetail(
        String type,
        String title,
        int status,
        String detail,
        String instance,
        List<FieldErrorDto> errors
) {
}

13. Pagination

Katta list endpointlarda pagination majburiy.

Yomon:

GET /api/orders

Agar 1 million order bo‘lsa, server qiynaladi.

Yaxshi:

GET /api/orders?page=0&size=20

Spring:

@GetMapping
public Page<OrderDto> getOrders(Pageable pageable) {
    return orderService.getOrders(pageable);
}

14. Page response’ni custom qilish

Spring Pageni to‘g‘ridan-to‘g‘ri qaytarish mumkin, lekin API contract juda katta bo‘lib ketadi. Custom response yaxshiroq.

public record PageResponse<T>(
        List<T> content,
        int page,
        int size,
        long totalElements,
        int totalPages,
        boolean last
) {
    public static <T> PageResponse<T> from(Page<T> page) {
        return new PageResponse<>(
                page.getContent(),
                page.getNumber(),
                page.getSize(),
                page.getTotalElements(),
                page.getTotalPages(),
                page.isLast()
        );
    }
}

Controller:

@GetMapping
public PageResponse<OrderDto> getOrders(Pageable pageable) {
    return PageResponse.from(orderService.getOrders(pageable));
}

Response:

{
  "content": [
    {
      "id": 1,
      "status": "NEW"
    }
  ],
  "page": 0,
  "size": 20,
  "totalElements": 125,
  "totalPages": 7,
  "last": false
}

15. Filtering

Filtering query parameter orqali bo‘ladi.

GET /api/orders?status=PAID
GET /api/orders?status=PAID&from=2026-01-01&to=2026-01-31
GET /api/users?name=ali&active=true

Request object qilish mumkin:

public record OrderFilterRequest(
        OrderStatus status,
        LocalDate from,
        LocalDate to
) {
}

Controller:

@GetMapping
public PageResponse<OrderDto> getOrders(
        OrderFilterRequest filter,
        Pageable pageable
) {
    return PageResponse.from(orderService.getOrders(filter, pageable));
}

16. Sorting

GET /api/orders?sort=createdAt,desc
GET /api/users?sort=name,asc

Spring Pageable buni qo‘llaydi:

@GetMapping
public PageResponse<UserDto> getUsers(Pageable pageable) {
    return PageResponse.from(userService.getUsers(pageable));
}

Lekin production’da sort fieldlarni whitelist qilish yaxshi.

Nega?

GET /api/users?sort=password,asc

Bunday fieldlarga sort qilish xavfli yoki keraksiz bo‘lishi mumkin.

Yaxshiroq:

private static final Set<String> ALLOWED_SORT_FIELDS = Set.of(
        "id",
        "name",
        "createdAt"
);

17. Keyset / Cursor Pagination

Katta tablelarda offset pagination sekinlashadi.

Offset:

GET /api/orders?page=5000&size=20

DB oldingi qatorlarni sanab o‘tadi.

Keyset:

GET /api/orders?cursor=1000&size=20

SQL:

SELECT *
FROM orders
WHERE id < :cursor
ORDER BY id DESC
LIMIT :size;

Response:

{
  "content": [
    {
      "id": 999,
      "status": "PAID"
    }
  ],
  "nextCursor": 980,
  "hasNext": true
}

DTO:

public record CursorPageResponse<T>(
        List<T> content,
        Long nextCursor,
        boolean hasNext
) {
}

18. Idempotency

Idempotency - bitta request takror yuborilsa ham, natija xavfsiz bo‘lishi.

Masalan, payment yaratishda client timeout oldi va requestni qayta yubordi. Agar idempotency bo‘lmasa, ikki marta payment bo‘lishi mumkin.

Header:

Idempotency-Key: 6d4c8c20-7a47-4b0d-9c16

Server shu key bo‘yicha oldingi natijani saqlaydi.

@PostMapping("/payments")
public PaymentResponse createPayment(
        @RequestHeader("Idempotency-Key") String idempotencyKey,
        @Valid @RequestBody CreatePaymentRequest request
) {
    return paymentService.create(idempotencyKey, request);
}

Qayerda kerak?

payment
order creation
money transfer
external API callback
retry bo‘ladigan POST requestlar

19. PUT vs PATCH

PUT

Resource’ni to‘liq almashtirish.

PUT /api/users/1
{
  "name": "Ali",
  "email": "ali@example.com",
  "phone": "+998901234567"
}

Agar phone yuborilmasa, u bo‘sh/null bo‘lib qolishi mumkin, chunki PUT to‘liq update sifatida qaraladi.


PATCH

Qisman update.

PATCH /api/users/1/status
{
  "status": "BLOCKED"
}

Yoki:

PATCH /api/users/1
{
  "phone": "+998901234567"
}

PATCH’da faqat berilgan fieldlar o‘zgaradi.


20. API Versioning

API o‘zgarganda eski clientlar buzilmasligi kerak.

Eng oddiy usul:

/api/v1/users
/api/v2/users

Controller:

@RestController
@RequestMapping("/api/v1/users")
public class UserV1Controller {
}

Yangi versiya:

@RestController
@RequestMapping("/api/v2/users")
public class UserV2Controller {
}

Versioning strategiyalari

Strategiya

Misol

Izoh

URL version

/api/v1/users

Oddiy va tushunarli

Header version

X-API-Version: 1

URL toza, lekin debug qiyinroq

Accept header

Accept: application/vnd.app.v1+json

Enterprise uslub

Query param

/users?version=1

Kamroq tavsiya qilinadi

Oddiy Spring Boot loyihalarda URL version yetarli.


21. Backward Compatibility

API’ni o‘zgartirishda ehtiyot bo‘lish kerak.

Xavfsiz o‘zgarishlar

response’ga yangi optional field qo‘shish
yangi endpoint qo‘shish
yangi query param qo‘shish

Masalan:

{
  "id": 1,
  "name": "Ali",
  "email": "ali@example.com",
  "phone": "+998901234567"
}

Agar eski client phoneni ishlatmasa, buzilmaydi.


Breaking changes

field nomini o‘zgartirish
fieldni olib tashlash
status code o‘zgartirish
response structure o‘zgartirish
required field qo‘shish
enum qiymatini olib tashlash

Bular uchun yangi API version kerak bo‘lishi mumkin.


22. OpenAPI / Swagger

API documentation uchun ishlatiladi.

Spring Boot’da odatda springdoc-openapi ishlatiladi.

Gradle:

implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:2.6.0")

Endpoint:

/swagger-ui/index.html
/v3/api-docs

Controller annotation:

@Tag(name = "Users", description = "User management API")
@RestController
@RequestMapping("/api/v1/users")
public class UserController {

    @Operation(summary = "Create user", description = "Creates a new user")
    @ApiResponses({
            @ApiResponse(responseCode = "201", description = "User created"),
            @ApiResponse(responseCode = "400", description = "Validation error")
    })
    @PostMapping
    public ResponseEntity<UserDto> create(@Valid @RequestBody CreateUserRequest request) {
        UserDto created = userService.create(request);

        return ResponseEntity
                .created(URI.create("/api/v1/users/" + created.id()))
                .body(created);
    }
}

23. Swagger’da security

JWT ishlatilsa Swagger’da token qo‘yish uchun config yoziladi.

@Configuration
public class OpenApiConfig {

    @Bean
    public OpenAPI openAPI() {
        String securitySchemeName = "bearerAuth";

        return new OpenAPI()
                .addSecurityItem(new SecurityRequirement().addList(securitySchemeName))
                .components(new Components()
                        .addSecuritySchemes(securitySchemeName,
                                new SecurityScheme()
                                        .name(securitySchemeName)
                                        .type(SecurityScheme.Type.HTTP)
                                        .scheme("bearer")
                                        .bearerFormat("JWT")));
    }
}

24. HTTP Caching Headers

Agar data tez-tez o‘zgarmasa, caching headerlar performance beradi.

Muhim headerlar:

Header

Vazifasi

Cache-Control

Cache qoidasi

ETag

Resource versiya belgisi

Last-Modified

Oxirgi o‘zgarish vaqti

If-None-Match

Clientdagi ETag bilan tekshirish

If-Modified-Since

Sana bo‘yicha tekshirish


Cache-Control

Masalan, public product categorylar kam o‘zgaradi.

Cache-Control: public, max-age=3600

Bu 1 soat cache qilish mumkin degani.

Sensitive data uchun:

Cache-Control: no-store

Masalan:

profile
payment info
personal data
token response

25. ETag

ETag resource versiyasini bildiradi.

Birinchi request:

GET /api/products/1

Response:

200 OK
ETag: "product-1-v5"

Keyingi requestda client yuboradi:

If-None-Match: "product-1-v5"

Agar product o‘zgarmagan bo‘lsa:

304 Not Modified

Body qaytmaydi. Traffic kamayadi.

Spring misol:

@GetMapping("/{id}")
public ResponseEntity<ProductDto> getProduct(@PathVariable Long id) {
    ProductDto product = productService.getById(id);

    String etag = "\"" + product.id() + "-" + product.version() + "\"";

    return ResponseEntity.ok()
            .eTag(etag)
            .body(product);
}

26. Security API Design

API design’da security ham bor.

Muhim qoidalar:

password response’da qaytmasin
token logga chiqmasin
sensitive endpointlar protected bo‘lsin
admin endpointlar role bilan cheklansin
CORS aniq sozlansin
rate limit bo‘lsin
request size limit bo‘lsin

Yomon response:

{
  "id": 1,
  "email": "ali@example.com",
  "password": "$2a$10$..."
}

Yaxshi response:

{
  "id": 1,
  "email": "ali@example.com"
}

27. Rate Limiting

API’ni spam yoki brute-force’dan himoya qilish uchun rate limit kerak.

Masalan:

login endpoint: 5 request / minute
SMS send endpoint: 3 request / minute
public search: 60 request / minute

Java/Spring’da ishlatilishi mumkin:

Bucket4j
Resilience4j RateLimiter
API Gateway rate limit
Nginx rate limit
Cloudflare rate limit

28. API Response structure

Kichik loyihalarda to‘g‘ridan-to‘g‘ri DTO qaytarish normal:

{
  "id": 1,
  "name": "Ali"
}

Lekin ba’zi jamoalar umumiy wrapper ishlatadi:

{
  "success": true,
  "data": {
    "id": 1,
    "name": "Ali"
  }
}

Bu shart emas. Muhimi - consistent bo‘lish.

Bir endpoint wrapper bilan, boshqasi wrappersiz bo‘lsa yomon.


29. Naming convention

Field nomlari odatda JSON’da camelCase bo‘ladi:

{
  "firstName": "Ali",
  "createdAt": "2026-05-20T10:00:00Z"
}

Java’da ham:

public record UserDto(
        String firstName,
        Instant createdAt
) {
}

30. Date/Time API Design

Date/time’da timezone muhim.

Yaxshi format:

{
  "createdAt": "2026-05-20T10:15:30Z"
}

Backend’da:

Instant createdAt;

Agar local sana kerak bo‘lsa:

LocalDate birthDate;

Tavsiya:

Holat

Java type

Audit time

Instant

Birth date

LocalDate

Business local time

LocalDateTime + timezone context

Scheduled event

ZonedDateTime


31. Id format

Oddiy ichki API’da numeric id:

GET /api/users/123

Public API’da ba’zida UUID yaxshi:

GET /api/orders/8f5a6a7b-6420-4c20-9cb6-2fd2f3d5e123

Numeric ID muammosi:

incremental id taxmin qilinadi
scraping osonlashadi
business hajm sezilishi mumkin

Lekin ichki systemlarda numeric ID normal.


32. HATEOAS

HATEOAS - response ichida keyingi mumkin bo‘lgan linklar berish.

Misol:

{
  "id": 1,
  "status": "NEW",
  "_links": {
    "self": {
      "href": "/api/orders/1"
    },
    "cancel": {
      "href": "/api/orders/1/cancel"
    },
    "pay": {
      "href": "/api/orders/1/pay"
    }
  }
}

Afzalligi:

client keyingi actionlarni response’dan biladi
API discoverable bo‘ladi

Kamchiligi:

ko‘p backend loyihalarda ortiqcha murakkablik
frontend/mobile uchun har doim ham kerak emas

Amaliyotda ko‘p jamoalar to‘liq HATEOAS ishlatmaydi. Lekin conceptni bilish kerak.


33. Action endpointlar

REST’da action endpointlar ham bo‘ladi, lekin ehtiyot bilan.

Masalan, order cancel qilish:

Variant 1:

PATCH /api/orders/1/status
{
  "status": "CANCELLED"
}

Variant 2:

POST /api/orders/1/cancel

Ikkinchi variant ham amaliyotda ko‘p ishlatiladi, chunki cancel oddiy status update emas, business action bo‘lishi mumkin:

refund qilish
stock qaytarish
notification yuborish
audit yozish

Demak, action endpoint ishlatish yomon emas. Faqat nomi aniq va consistent bo‘lsin.


34. File upload API

File upload uchun multipart/form-data ishlatiladi.

@PostMapping(value = "/avatar", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public UserDto uploadAvatar(
        @RequestParam("file") MultipartFile file
) {
    return userService.uploadAvatar(file);
}

Muhim tekshiruvlar:

file size
file extension
MIME type
virus scan
storage path traversal
public/private access

35. Bulk API

Bir nechta itemni bitta requestda yaratish:

POST /api/products/bulk
{
  "items": [
    {
      "name": "Product 1",
      "price": 100
    },
    {
      "name": "Product 2",
      "price": 200
    }
  ]
}

Response partial success bo‘lishi mumkin:

{
  "successCount": 1,
  "failedCount": 1,
  "errors": [
    {
      "index": 1,
      "message": "Price invalid"
    }
  ]
}

Bulk API’da transaction strategiyani oldindan hal qilish kerak:

hammasi muvaffaqiyatli bo‘lsa commit
yoki ayrimlari fail bo‘lsa ham qolganlari saqlansin

36. API Contract Testing

Agar backend API’dan frontend/mobile yoki boshqa service foydalansa, contract muhim.

Contract testing uchun:

Spring Cloud Contract
Pact
OpenAPI schema validation

Maqsad:

Backend response structure’ni o‘zgartirib, clientni buzib qo‘ymaslik

37. Logging va Correlation ID

Har requestga correlationId berish debuggingni osonlashtiradi.

Header:

X-Correlation-ID: 9f8d7a6c

Response’da ham qaytarish mumkin.

Log:

correlationId=9f8d7a6c userId=123 action=createOrder

Filter:

@Component
public class CorrelationIdFilter extends OncePerRequestFilter {

    private static final String HEADER = "X-Correlation-ID";

    @Override
    protected void doFilterInternal(
            HttpServletRequest request,
            HttpServletResponse response,
            FilterChain filterChain
    ) throws ServletException, IOException {
        String correlationId = request.getHeader(HEADER);

        if (correlationId == null || correlationId.isBlank()) {
            correlationId = UUID.randomUUID().toString();
        }

        MDC.put("correlationId", correlationId);
        response.setHeader(HEADER, correlationId);

        try {
            filterChain.doFilter(request, response);
        } finally {
            MDC.remove("correlationId");
        }
    }
}

38. API Design’da ko‘p uchraydigan xatolar

1. Verb bilan URL yozish

Yomon:

POST /api/createUser
POST /api/deleteOrder
GET /api/getProducts

Yaxshi:

POST /api/users
DELETE /api/orders/1
GET /api/products

2. Har doim 200 OK qaytarish

Yomon:

200 OK
{
  "success": false,
  "message": "User not found"
}

Yaxshi:

404 Not Found
{
  "type": "https://api.example.com/errors/user-not-found",
  "title": "User not found",
  "status": 404,
  "detail": "User not found with id: 10"
}

3. Entity qaytarish

Yomon:

public User getUser() {}

Yaxshi:

public UserDto getUser() {}

4. Pagination yo‘qligi

Yomon:

GET /api/orders

Yaxshi:

GET /api/orders?page=0&size=20

5. Error format inconsistent

Yomon:

{
  "error": "not found"
}

Boshqa endpoint:

{
  "message": "validation failed",
  "fields": []
}

Yaxshi: hamma error bitta formatda.


6. Breaking change’ni eski versionga qilish

Yomon:

/api/v1/users response fieldlarini o‘zgartirish

Yaxshi:

/api/v2/users chiqarish

39. Real Order API design

Order yaratish

POST /api/v1/orders

Request:

{
  "userId": 10,
  "items": [
    {
      "productId": 5,
      "quantity": 2
    }
  ],
  "paymentType": "CLICK"
}

Response:

201 Created
Location: /api/v1/orders/100
{
  "id": 100,
  "status": "NEW",
  "totalAmount": 250000,
  "createdAt": "2026-05-20T10:15:30Z"
}

Order list

GET /api/v1/orders?status=PAID&page=0&size=20&sort=createdAt,desc

Response:

{
  "content": [
    {
      "id": 100,
      "status": "PAID",
      "totalAmount": 250000,
      "createdAt": "2026-05-20T10:15:30Z"
    }
  ],
  "page": 0,
  "size": 20,
  "totalElements": 120,
  "totalPages": 6,
  "last": false
}

Order detail

GET /api/v1/orders/100

Response:

{
  "id": 100,
  "status": "PAID",
  "totalAmount": 250000,
  "items": [
    {
      "productId": 5,
      "productName": "Keyboard",
      "quantity": 2,
      "price": 125000
    }
  ],
  "createdAt": "2026-05-20T10:15:30Z"
}

Order cancel

POST /api/v1/orders/100/cancel

Request:

{
  "reason": "User requested cancellation"
}

Response:

{
  "id": 100,
  "status": "CANCELLED"
}

40. Controller + Service structure

Controller yupqa bo‘lsin:

@RestController
@RequestMapping("/api/v1/orders")
public class OrderController {

    private final OrderService orderService;

    public OrderController(OrderService orderService) {
        this.orderService = orderService;
    }

    @PostMapping
    public ResponseEntity<OrderDto> create(@Valid @RequestBody CreateOrderRequest request) {
        OrderDto created = orderService.create(request);

        return ResponseEntity
                .created(URI.create("/api/v1/orders/" + created.id()))
                .body(created);
    }

    @GetMapping("/{id}")
    public OrderDetailDto getById(@PathVariable Long id) {
        return orderService.getDetail(id);
    }

    @PostMapping("/{id}/cancel")
    public OrderDto cancel(
            @PathVariable Long id,
            @Valid @RequestBody CancelOrderRequest request
    ) {
        return orderService.cancel(id, request);
    }
}

Service business logicni ushlaydi:

@Service
public class OrderService {

    @Transactional
    public OrderDto create(CreateOrderRequest request) {
        // validate user
        // validate products
        // create order
        // calculate total
        // save
        // publish event
        return result;
    }

    @Transactional(readOnly = true)
    public OrderDetailDto getDetail(Long id) {
        // fetch order with items
        return result;
    }

    @Transactional
    public OrderDto cancel(Long id, CancelOrderRequest request) {
        // business cancel logic
        return result;
    }
}

41. Interview savollari

REST nima?

HTTP methodlar orqali resource’lar bilan ishlash uslubi.

GET, POST, PUT, PATCH, DELETE farqi?

GET o‘qish, POST yaratish, PUT to‘liq update, PATCH qisman update, DELETE o‘chirish.

401 va 403 farqi?

401 - user authenticated emas yoki token noto‘g‘ri.
403 - user authenticated, lekin ruxsati yo‘q.

400 va 422 farqi?

400 - request syntax/format noto‘g‘ri.
422 - request format to‘g‘ri, lekin semantic/business validationdan o‘tmadi.

Ko‘p loyihalar validation uchun 400 ishlatadi. Muhimi - consistent bo‘lish.

PUT va PATCH farqi?

PUT resource’ni to‘liq almashtiradi.
PATCH resource’ni qisman yangilaydi.

RFC 7807 nima?

HTTP API errorlarni standart Problem Details formatida qaytarish uslubi.

OpenAPI nima?

API contract va documentation format. Swagger UI orqali endpointlarni ko‘rish va test qilish mumkin.

API versioning nima uchun kerak?

Eski clientlarni buzmasdan API’ni o‘zgartirish uchun.

Idempotency nima?

Bir xil request takror yuborilsa ham, nojo‘ya takroriy effect bo‘lmasligi.

HATEOAS nima?

Response ichida resource bilan bog‘liq link/actionlarni qaytarish yondashuvi.


42. Java Developer checklist

REST & API Design bo‘yicha siz quyidagilarni bilishingiz kerak:

REST resource naming
HTTP methods
status codes
request/response DTO
validation
global exception handler
RFC 7807 Problem Details
pagination
filtering
sorting
keyset pagination
idempotency
PUT vs PATCH
API versioning
backward compatibility
OpenAPI/Swagger
HTTP caching headers
ETag
security basics in API design
rate limiting
correlation ID
HATEOAS concept

Qisqa xulosa

REST & API Design - endpoint ochishdan kattaroq mavzu.

Junior ko‘pincha shunday qiladi:

POST /createOrder
GET /getOrders
POST /deleteOrder

Middle esa shunday loyihalaydi:

POST /api/v1/orders
GET /api/v1/orders?page=0&size=20
GET /api/v1/orders/100
POST /api/v1/orders/100/cancel
DELETE /api/v1/orders/100

Eng muhim 5 ta joy:

1. Resource-oriented URL
2. To‘g‘ri HTTP method va status code
3. DTO + validation + consistent error response
4. Pagination/filter/sort
5. Versioning + OpenAPI documentation