JSON API là gì? Hướng dẫn sử dụng JSON trong REST API

Khi một website cần lấy danh sách sản phẩm, ứng dụng cần tải thông tin tài khoản hoặc hệ thống quản lý phải trao đổi dữ liệu với một dịch vụ khác, các thành phần đó cần một cách giao tiếp thống nhất. JSON API thường xuất hiện trong chính quá trình này, đặc biệt ở các hệ thống xây dựng theo kiến trúc REST.

Điểm đáng chú ý là JSON và REST API không phải là một khái niệm. JSON là định dạng dữ liệu, còn REST API là cách tổ chức việc giao tiếp giữa các hệ thống thông qua HTTP. Khi kết hợp, REST API có thể dùng JSON để nhận dữ liệu từ client và trả dữ liệu về cho client.

Hiểu đúng mối quan hệ này sẽ giúp việc xây dựng API rõ ràng hơn, từ thiết kế endpoint, gửi request, đọc response cho đến xử lý lỗi và bảo vệ dữ liệu.

JSON API là gì? Hướng dẫn sử dụng JSON trong REST API
JSON API là gì? Hướng dẫn sử dụng JSON trong REST API

JSON API thực chất là gì?

Có thể hình dung một API như một điểm giao tiếp giữa hai phần mềm. Một ứng dụng gửi yêu cầu đến máy chủ, máy chủ xử lý yêu cầu rồi trả kết quả về. JSON thường được sử dụng để đóng gói phần dữ liệu trong request hoặc response.

Ví dụ, một ứng dụng muốn lấy thông tin một sản phẩm có thể gửi yêu cầu đến một endpoint như /api/products/15. Máy chủ xử lý yêu cầu và trả về dữ liệu sản phẩm dưới dạng JSON.

{
  "id": 15,
  "name": "Bàn làm việc",
  "price": 2500000,
  "available": true
}

Trong ví dụ trên, JSON không quyết định API phải sử dụng URL nào, HTTP method nào hay máy chủ xử lý request ra sao. Nó chỉ đảm nhiệm việc biểu diễn dữ liệu theo một cấu trúc mà cả hai bên có thể hiểu.

Đây cũng là lý do không nên đồng nhất hai khái niệm JSON APIREST API. Một REST API có thể sử dụng JSON, nhưng JSON bản thân nó không phải là REST.

JSON và REST API khác nhau ở điểm nào?

Thành phần Vai trò
JSON Biểu diễn và truyền dữ liệu có cấu trúc
REST Định hướng cách thiết kế và tổ chức API
HTTP Cơ chế truyền request và response giữa client với server
Endpoint Địa chỉ mà client sử dụng để truy cập một tài nguyên

Ví dụ một API quản lý sản phẩm có thể có các endpoint như:

GET    /api/products
GET    /api/products/15
POST   /api/products
PUT    /api/products/15
DELETE /api/products/15

Ở đây, HTTP method thể hiện hành động mà client muốn thực hiện, còn JSON có thể được sử dụng làm dữ liệu truyền đi.

Chẳng hạn khi tạo sản phẩm bằng POST, client có thể gửi:

{
  "name": "Ghế văn phòng",
  "price": 1800000,
  "stock": 25
}

Server tiếp nhận JSON, kiểm tra dữ liệu, thực hiện nghiệp vụ rồi trả response phù hợp.

Cấu trúc JSON thường gặp khi làm API

JSON có cấu trúc khá đơn giản nhưng khi dùng trong API, cách tổ chức dữ liệu ảnh hưởng trực tiếp đến khả năng đọc, xử lý và mở rộng hệ thống.

Object với cặp key và value

Object là cấu trúc phổ biến nhất trong response API. Dữ liệu được biểu diễn bằng cặp keyvalue.

{
  "id": 101,
  "name": "Nguyễn Văn A",
  "email": "a@example.com"
}

Key thường mô tả ý nghĩa của dữ liệu, trong khi value có thể là chuỗi, số, boolean, null, object hoặc array.

Array khi API trả về danh sách

Khi cần trả nhiều bản ghi, JSON thường sử dụng array.

[
  {
    "id": 1,
    "name": "Sản phẩm A"
  },
  {
    "id": 2,
    "name": "Sản phẩm B"
  }
]

Trong API thực tế, danh sách thường được đặt bên trong một object để có thể bổ sung thêm thông tin như tổng số bản ghi, phân trang hoặc trạng thái xử lý.

{
  "data": [
    {
      "id": 1,
      "name": "Sản phẩm A"
    },
    {
      "id": 2,
      "name": "Sản phẩm B"
    }
  ],
  "total": 2
}

Kiểu dữ liệu cần chú ý

JSON hỗ trợ một số kiểu dữ liệu cơ bản gồm string, number, boolean, null, object và array.

Điều quan trọng khi thiết kế API là phải thống nhất kiểu dữ liệu. Ví dụ, nếu price được quy định là số thì không nên lúc trả về số, lúc lại trả về chuỗi chỉ vì một endpoint được viết khác cách.

{
  "price": 2500000,
  "stock": 10,
  "available": true,
  "description": null
}

Sự nhất quán này đặc biệt quan trọng với frontend, mobile app và các hệ thống bên thứ ba vì phía client thường dựa vào kiểu dữ liệu để xử lý tự động.

REST API sử dụng JSON trong request và response như thế nào?

Một giao tiếp API thường có hai chiều dữ liệu. Client gửi request đến server và server trả lại response. JSON có thể xuất hiện ở cả hai chiều, nhưng không phải request nào cũng cần body JSON.

GET thường dùng để đọc dữ liệu

GET thường được dùng để lấy tài nguyên. Với những request đơn giản, dữ liệu có thể được truyền qua URL hoặc query string.

GET /api/products?page=1&limit=20

Server có thể trả về:

{
  "data": [
    {
      "id": 1,
      "name": "Bàn làm việc",
      "price": 2500000
    },
    {
      "id": 2,
      "name": "Ghế văn phòng",
      "price": 1800000
    }
  ],
  "page": 1,
  "limit": 20
}

GET thường không cần gửi JSON body. Đây là một điểm người mới làm API dễ nhầm khi cho rằng mọi request đều phải có JSON.

POST dùng để gửi dữ liệu mới

POST thường được sử dụng khi client muốn tạo một tài nguyên mới. Dữ liệu gửi lên thường nằm trong request body dưới dạng JSON.

{
  "name": "Tủ hồ sơ",
  "price": 3200000,
  "stock": 12
}

Server có thể kiểm tra dữ liệu, lưu vào cơ sở dữ liệu và trả về tài nguyên vừa tạo:

{
  "id": 25,
  "name": "Tủ hồ sơ",
  "price": 3200000,
  "stock": 12
}

PUT và PATCH khi cập nhật dữ liệu

PUT thường được dùng khi client muốn cập nhật tài nguyên theo một biểu diễn đầy đủ hoặc thay thế tài nguyên. PATCH phù hợp hơn với trường hợp chỉ thay đổi một phần dữ liệu.

Ví dụ PATCH chỉ thay đổi giá:

{
  "price": 3400000
}

Cách lựa chọn PUT hay PATCH phụ thuộc vào thiết kế API, nhưng điều quan trọng hơn là hành vi của endpoint phải nhất quán để phía client có thể sử dụng mà không phải đoán.

DELETE dùng để xóa tài nguyên

DELETE thường xác định tài nguyên cần xóa thông qua URL:

DELETE /api/products/25

Trong trường hợp này, server có thể không cần nhận JSON body. Response có thể chứa thông tin xác nhận hoặc sử dụng HTTP status phù hợp để biểu thị kết quả.

Header Content-Type có vai trò gì khi gửi JSON?

Khi client gửi JSON trong request body, server cần biết nội dung đó đang được mã hóa theo định dạng nào. Header phổ biến nhất cho trường hợp này là:

Content-Type: application/json

Nếu thiếu hoặc khai báo sai header, server có thể không xử lý body theo cách mà client mong muốn.

Ví dụ một request có thể có dạng:

POST /api/products
Content-Type: application/json

{
  "name": "Bàn họp",
  "price": 4500000
}

Ngoài Content-Type, API còn thường sử dụng Accept để thể hiện định dạng response mà client mong muốn:

Accept: application/json

Hai header này phục vụ hai mục đích khác nhau: Content-Type mô tả dữ liệu đang gửi, còn Accept thể hiện định dạng mà client mong muốn nhận.

Gọi REST API bằng JavaScript fetch()

Trong trình duyệt, JavaScript có thể giao tiếp với REST API thông qua fetch(). Đây là cách phổ biến để website gửi request đến server mà không cần tải lại toàn bộ trang.

Quy trình cơ bản thường gồm bốn bước: xác định endpoint, chọn HTTP method, thiết lập header và xử lý dữ liệu trả về.

Đọc dữ liệu bằng GET

Giả sử hệ thống có endpoint cung cấp danh sách sản phẩm tại /api/products. Một request GET đơn giản có thể được thực hiện như sau:

fetch('/api/products')
  .then(response => response.json())
  .then(data => {
    console.log(data);
  })
  .catch(error => {
    console.error(error);
  });

Phương thức response.json() đọc phần body của response và chuyển chuỗi JSON thành giá trị JavaScript để chương trình có thể truy cập từng thuộc tính.

Với cú pháp async/await, đoạn code có thể dễ đọc hơn:

async function getProducts() {
  try {
    const response = await fetch('/api/products');
    const data = await response.json();

    console.log(data);
  } catch (error) {
    console.error(error);
  }
}

getProducts();

Cách viết này đặc biệt thuận tiện khi một chức năng phải thực hiện nhiều request hoặc có nhiều bước xử lý liên tiếp.

Gửi dữ liệu bằng POST

Khi tạo một tài nguyên mới, JavaScript có thể đưa object thành JSON bằng JSON.stringify() rồi gửi trong request body.

async function createProduct() {
  const product = {
    name: 'Bàn làm việc',
    price: 2500000,
    stock: 10
  };

  try {
    const response = await fetch('/api/products', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Accept': 'application/json'
      },
      body: JSON.stringify(product)
    });

    const data = await response.json();

    console.log(data);
  } catch (error) {
    console.error(error);
  }
}

createProduct();

Có hai thao tác chuyển đổi quan trọng cần phân biệt. JSON.stringify() biến dữ liệu JavaScript thành chuỗi JSON để gửi đi, trong khi response.json() thực hiện chiều ngược lại với dữ liệu nhận về.

JavaScript object
       ↓
JSON.stringify()
       ↓
JSON request
       ↓
REST API
       ↓
JSON response
       ↓
response.json()
       ↓
JavaScript object

Xử lý response đúng cách thay vì chỉ đọc JSON

Một lỗi phổ biến khi mới sử dụng fetch() là chỉ kiểm tra phần JSON mà bỏ qua HTTP status. Điều này có thể khiến chương trình hiểu nhầm một response lỗi thành response thành công.

Ví dụ server trả về mã 404 cùng JSON mô tả lỗi. Bản thân fetch() vẫn có thể nhận được response bình thường. Vì vậy, nên kiểm tra response.ok trước khi coi request là thành công.

async function getProduct(id) {
  try {
    const response = await fetch(`/api/products/${id}`);

    if (!response.ok) {
      throw new Error(`HTTP error: ${response.status}`);
    }

    const product = await response.json();

    console.log(product);
  } catch (error) {
    console.error('Không thể lấy sản phẩm:', error);
  }
}

getProduct(15);

Cách này giúp phân biệt rõ hai tình huống: request không thể thực hiện do vấn đề kết nối và server đã phản hồi nhưng cho biết request không hợp lệ hoặc tài nguyên không tồn tại.

Phân biệt lỗi HTTP và lỗi kết nối

Đây là điểm rất quan trọng khi xây dựng giao diện sử dụng API. fetch() không tự động xem các mã HTTP như 400, 404 hoặc 500 là lỗi JavaScript. Vì vậy, lập trình viên cần chủ động kiểm tra response.

Tình huống Cách xử lý
200 - 299 Request thường được xem là thành công
400 Kiểm tra dữ liệu hoặc tham số client gửi lên
401 Kiểm tra trạng thái xác thực
403 Kiểm tra quyền truy cập
404 Kiểm tra endpoint hoặc tài nguyên cần truy cập
500 Kiểm tra lỗi phía server

Không nên chỉ hiển thị một thông báo chung như “API bị lỗi”. Giao diện có thể dựa vào status code để đưa ra phản hồi phù hợp, chẳng hạn yêu cầu đăng nhập lại khi phiên xác thực hết hạn hoặc thông báo rằng sản phẩm không còn tồn tại.

Gửi PUT và PATCH để cập nhật dữ liệu

Khi người dùng chỉnh sửa thông tin, client có thể gửi dữ liệu JSON đến endpoint tương ứng. Với PUT, request thường thể hiện một lần cập nhật tài nguyên; với PATCH, dữ liệu có thể chỉ chứa những trường cần thay đổi.

Ví dụ cập nhật một phần thông tin sản phẩm bằng PATCH:

async function updateProduct(id) {
  const changes = {
    price: 2800000,
    stock: 15
  };

  const response = await fetch(`/api/products/${id}`, {
    method: 'PATCH',
    headers: {
      'Content-Type': 'application/json',
      'Accept': 'application/json'
    },
    body: JSON.stringify(changes)
  });

  if (!response.ok) {
    throw new Error(`HTTP error: ${response.status}`);
  }

  return await response.json();
}

Việc chỉ gửi các trường thực sự cần thay đổi có thể làm request gọn hơn, nhưng server phải được thiết kế để xử lý PATCH đúng nghĩa. Client không nên tự suy đoán rằng mọi API đều hỗ trợ cách cập nhật này.

Xóa dữ liệu thông qua DELETE

DELETE thường xác định tài nguyên cần xóa trực tiếp từ URL.

async function deleteProduct(id) {
  const response = await fetch(`/api/products/${id}`, {
    method: 'DELETE',
    headers: {
      'Accept': 'application/json'
    }
  });

  if (!response.ok) {
    throw new Error(`HTTP error: ${response.status}`);
  }

  return true;
}

Không phải API nào cũng trả về một JSON object sau DELETE. Một số API chỉ sử dụng HTTP status để thông báo kết quả. Vì vậy, client cần biết trước quy ước response của API thay vì luôn gọi response.json().

Thiết kế dữ liệu response dễ sử dụng

Một API tốt không chỉ trả đúng dữ liệu mà còn nên có cấu trúc nhất quán. Khi frontend phải xử lý hàng chục endpoint, sự đồng nhất của response giúp giảm đáng kể logic kiểm tra và xử lý riêng lẻ.

Ví dụ một response thành công có thể được tổ chức:

{
  "data": {
    "id": 15,
    "name": "Bàn làm việc",
    "price": 2500000
  }
}

Trong khi response danh sách có thể giữ cùng cách tổ chức:

{
  "data": [
    {
      "id": 15,
      "name": "Bàn làm việc",
      "price": 2500000
    },
    {
      "id": 16,
      "name": "Ghế văn phòng",
      "price": 1800000
    }
  ]
}

Khi API có phân trang, các thông tin bổ sung có thể được đặt cạnh data:

{
  "data": [
    {
      "id": 15,
      "name": "Bàn làm việc"
    }
  ],
  "pagination": {
    "page": 1,
    "limit": 20,
    "total": 125
  }
}

Cấu trúc như vậy giúp client phân biệt rõ dữ liệu chính với thông tin điều khiển hoặc metadata.

Thiết kế response lỗi có ý nghĩa

Response lỗi nên giúp phía client biết chuyện gì đã xảy ra và có thể xử lý như thế nào. Một JSON lỗi tốt không nhất thiết phải dài, nhưng cần nhất quán.

{
  "error": {
    "code": "PRODUCT_NOT_FOUND",
    "message": "Không tìm thấy sản phẩm"
  }
}

Đối với lỗi validation, API có thể trả thêm thông tin theo từng trường:

{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Dữ liệu không hợp lệ",
    "fields": {
      "name": "Tên sản phẩm không được để trống",
      "price": "Giá phải lớn hơn 0"
    }
  }
}

Điều này tốt hơn việc trả về một chuỗi lỗi chung chung vì frontend có thể xác định chính xác trường nào cần hiển thị thông báo.

Gửi JSON từ form lên REST API

Trong website thực tế, dữ liệu thường bắt đầu từ form HTML. JavaScript lấy giá trị từ form, kiểm tra dữ liệu rồi chuyển thành JSON trước khi gửi đến server.

const form = document.querySelector('#productForm');

form.addEventListener('submit', async function (event) {
  event.preventDefault();

  const product = {
    name: form.elements.name.value.trim(),
    price: Number(form.elements.price.value),
    stock: Number(form.elements.stock.value)
  };

  try {
    const response = await fetch('/api/products', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Accept': 'application/json'
      },
      body: JSON.stringify(product)
    });

    const data = await response.json();

    if (!response.ok) {
      throw new Error(data.error?.message || 'Không thể tạo sản phẩm');
    }

    console.log('Tạo sản phẩm thành công:', data);
  } catch (error) {
    console.error(error);
  }
});

Ở đây có một nguyên tắc quan trọng: client validation không thay thế server validation. JavaScript có thể kiểm tra dữ liệu để cải thiện trải nghiệm người dùng, nhưng server vẫn phải tự kiểm tra mọi dữ liệu nhận được từ client.

Xác thực khi REST API yêu cầu đăng nhập

API công khai có thể cho phép truy cập mà không cần đăng nhập, nhưng những tài nguyên riêng tư như tài khoản, đơn hàng hoặc dữ liệu quản trị thường phải được xác thực. Một cách phổ biến là client gửi token trong HTTP header.

Authorization: Bearer YOUR_ACCESS_TOKEN

JavaScript có thể gửi token cùng request:

async function getOrders() {
  const response = await fetch('/api/orders', {
    headers: {
      'Accept': 'application/json',
      'Authorization': 'Bearer YOUR_ACCESS_TOKEN'
    }
  });

  if (!response.ok) {
    throw new Error(`HTTP error: ${response.status}`);
  }

  return await response.json();
}

Không nên đưa khóa bí mật hoặc thông tin xác thực nhạy cảm trực tiếp vào mã JavaScript chạy trên trình duyệt. Bất kỳ dữ liệu nào được gửi xuống client đều có khả năng bị người dùng quan sát.

Với hệ thống thực tế, việc xác thực nên được thiết kế ngay từ đầu thay vì xây dựng API hoàn chỉnh rồi mới tìm cách bổ sung bảo mật.

CORS ảnh hưởng thế nào khi gọi API từ website?

CORS là cơ chế liên quan đến việc trình duyệt cho phép một website gửi request đến một origin khác. Đây là nguyên nhân thường gặp khi API hoạt động bình thường bằng công cụ kiểm thử nhưng lại bị trình duyệt chặn khi gọi từ frontend.

Ví dụ website chạy tại một domain nhưng API nằm trên domain khác. Server API cần trả về các HTTP header CORS phù hợp để trình duyệt xác định request đó có được phép hay không.

CORS không phải cơ chế xác thực người dùng. Việc cho phép một origin truy cập API cũng không có nghĩa API đã được bảo mật. Xác thực, phân quyền và CORS là những vấn đề khác nhau và cần được xử lý độc lập.

Query parameter giúp API linh hoạt hơn

Khi API trả về danh sách lớn, việc tải toàn bộ dữ liệu trong một request thường không hiệu quả. Query parameter cho phép client yêu cầu đúng phần dữ liệu cần thiết.

GET /api/products?page=2&limit=20

Ngoài phân trang, query parameter còn có thể phục vụ tìm kiếm, lọc hoặc sắp xếp:

GET /api/products?keyword=ban&category=office&sort=price_asc

Server cần xác định rõ ý nghĩa của từng tham số và quy định giá trị mặc định. Nếu client không truyền page, chẳng hạn, API có thể mặc định về trang đầu tiên.

Không nên để query parameter trở thành một tập hợp tùy tiện mà mỗi endpoint hiểu theo một cách khác nhau. Quy ước nhất quán sẽ giúp API dễ sử dụng và dễ bảo trì hơn.

Phân trang dữ liệu thay vì trả về toàn bộ danh sách

Giả sử cơ sở dữ liệu có hàng trăm nghìn sản phẩm. Trả tất cả sản phẩm trong một response JSON sẽ làm tăng thời gian xử lý, dung lượng truyền tải và bộ nhớ mà frontend phải sử dụng.

Một response phân trang có thể chứa dữ liệu cùng thông tin mô tả trang hiện tại:

{
  "data": [
    {
      "id": 101,
      "name": "Bàn làm việc"
    },
    {
      "id": 102,
      "name": "Ghế văn phòng"
    }
  ],
  "pagination": {
    "page": 3,
    "limit": 20,
    "total": 245
  }
}

Frontend dựa vào page, limittotal để xây dựng nút chuyển trang hoặc tải thêm dữ liệu.

Đối với những hệ thống có lượng dữ liệu rất lớn hoặc yêu cầu phân trang ổn định khi dữ liệu liên tục thay đổi, có thể cân nhắc kiểu phân trang dựa trên cursor thay vì chỉ dựa vào số trang.

Xử lý JSON trong PHP khi xây dựng API

PHP có sẵn các hàm để chuyển đổi qua lại giữa dữ liệu PHP và JSON. json_encode() thường được dùng để tạo response JSON, còn json_decode() dùng để đọc JSON nhận từ client.

Ví dụ server trả dữ liệu sản phẩm:

<?php
header('Content-Type: application/json; charset=utf-8');

$product = [
    'id' => 15,
    'name' => 'Bàn làm việc',
    'price' => 2500000
];

echo json_encode($product, JSON_UNESCAPED_UNICODE);

Khi client gửi JSON bằng POST, PHP có thể đọc request body thông qua php://input:

<?php
header('Content-Type: application/json; charset=utf-8');

$input = file_get_contents('php://input');
$data = json_decode($input, true);

$name = $data['name'] ?? '';
$price = $data['price'] ?? 0;

echo json_encode([
    'name' => $name,
    'price' => $price
], JSON_UNESCAPED_UNICODE);

Tham số true trong json_decode() giúp dữ liệu JSON object được chuyển thành associative array trong PHP. Nếu không truyền tham số này, kết quả thường được xử lý dưới dạng object.

Kiểm tra lỗi khi giải mã dữ liệu

Không nên mặc định rằng mọi request gửi đến server đều chứa JSON hợp lệ. Dữ liệu có thể bị thiếu, sai cú pháp hoặc không đúng cấu trúc mà API yêu cầu.

<?php
$input = file_get_contents('php://input');
$data = json_decode($input, true);

if (json_last_error() !== JSON_ERROR_NONE) {
    http_response_code(400);

    echo json_encode([
        'error' => [
            'code' => 'INVALID_JSON',
            'message' => 'Dữ liệu JSON không hợp lệ'
        ]
    ], JSON_UNESCAPED_UNICODE);

    exit;
}

Việc kiểm tra cú pháp JSON chỉ là bước đầu. Sau đó server vẫn phải kiểm tra kiểu dữ liệu, giá trị, quyền thao tác và các điều kiện nghiệp vụ.

Đừng nhầm JSON hợp lệ với dữ liệu hợp lệ

Đây là một trong những nguyên tắc quan trọng nhất khi xây dựng API.

Ví dụ JSON sau hoàn toàn hợp lệ về mặt cú pháp:

{
  "name": "",
  "price": -500000,
  "stock": "abc"
}

Nhưng một sản phẩm có tên rỗng, giá âm và số lượng tồn kho là chuỗi chữ có thể không hợp lệ đối với nghiệp vụ của hệ thống.

Do đó, quá trình xử lý request nên được tách thành nhiều lớp:

  1. Kiểm tra request có đúng định dạng hay không.
  2. Kiểm tra các trường bắt buộc.
  3. Kiểm tra kiểu dữ liệu.
  4. Kiểm tra giới hạn và giá trị cho phép.
  5. Kiểm tra quyền thực hiện thao tác.
  6. Kiểm tra các quy tắc nghiệp vụ.
  7. Sau cùng mới ghi hoặc thay đổi dữ liệu.

Cách làm này giúp API tránh được tình trạng dữ liệu xấu đi sâu vào hệ thống rồi mới phát hiện lỗi ở những bước phía sau.

Bảo mật dữ liệu khi thiết kế API

JSON chỉ là định dạng dữ liệu nên bản thân JSON không làm API an toàn hay không an toàn. Bảo mật phụ thuộc vào toàn bộ cách API tiếp nhận, xác thực, xử lý và trả dữ liệu.

Một API thực tế nên đặc biệt chú ý những vấn đề sau:

  • Sử dụng HTTPS khi truyền dữ liệu qua Internet.
  • Xác thực người dùng đối với tài nguyên cần bảo vệ.
  • Kiểm tra quyền trên từng tài nguyên, không chỉ kiểm tra người dùng đã đăng nhập.
  • Không trả về thông tin nhạy cảm không cần thiết.
  • Kiểm tra và giới hạn dữ liệu đầu vào.
  • Giới hạn tần suất request đối với những API có nguy cơ bị lạm dụng.
  • Không đưa secret hoặc khóa riêng tư vào JavaScript phía client.
  • Ghi log đủ để phát hiện sự cố nhưng tránh ghi trực tiếp token hoặc dữ liệu nhạy cảm.

Đặc biệt, không nên dựa vào việc ẩn endpoint để bảo vệ dữ liệu. Endpoint đã xuất hiện trong ứng dụng frontend thì về nguyên tắc người dùng có thể quan sát được. Quyền truy cập phải được kiểm tra ở phía server.

Tối ưu response để API nhẹ và dễ sử dụng

Response càng lớn thì client càng phải tải và xử lý nhiều dữ liệu. Vì vậy, API chỉ nên trả những trường thực sự cần thiết cho mục đích của request.

Ví dụ một màn hình danh sách sản phẩm có thể chỉ cần:

{
  "id": 15,
  "name": "Bàn làm việc",
  "price": 2500000,
  "thumbnail": "/images/product-15.jpg"
}

Không nhất thiết phải trả toàn bộ mô tả dài, lịch sử thay đổi, dữ liệu quản trị hoặc các trường nội bộ nếu màn hình đó không sử dụng chúng.

Đối với hệ thống lớn, việc thiết kế response hợp lý có thể giảm đáng kể lượng dữ liệu truyền tải và giúp giao diện phản hồi nhanh hơn.

Những lỗi thường gặp khi làm việc với JSON API

Gửi object JavaScript nhưng quên stringify

Request body cần chứa JSON phù hợp với Content-Type đã khai báo. Nếu truyền trực tiếp object JavaScript thay vì chuyển đổi bằng JSON.stringify(), dữ liệu gửi đi có thể không đúng định dạng mà server mong đợi.

Quên Content-Type

Server có thể không biết request body đang chứa JSON nếu client không khai báo Content-Type phù hợp.

Không kiểm tra response.ok

fetch() không tự động throw exception chỉ vì server trả về HTTP 400 hoặc 500. Client nên kiểm tra status trước khi coi request là thành công.

Luôn gọi response.json()

Một số response thành công có thể không chứa JSON body, chẳng hạn khi server trả về 204. Vì vậy, cần biết endpoint trả về loại response nào trước khi parse.

Chỉ kiểm tra dữ liệu ở frontend

Validation phía trình duyệt giúp trải nghiệm tốt hơn nhưng không thể thay thế validation phía server. Client hoàn toàn có thể bị sửa request trước khi gửi đến API.

Trả lỗi không nhất quán

Nếu endpoint này trả lỗi bằng message, endpoint khác lại dùng error_message và endpoint khác nữa dùng một chuỗi thuần túy, frontend sẽ phải viết nhiều trường hợp xử lý. Một cấu trúc lỗi thống nhất sẽ tốt hơn.

Quy trình xây dựng một REST API dùng JSON

Thay vì bắt đầu bằng việc viết code ngay, nên xác định cách API hoạt động từ trước. Một quy trình đơn giản có thể gồm:

  1. Xác định tài nguyên mà API quản lý.
  2. Đặt endpoint rõ ràng và nhất quán.
  3. Xác định HTTP method cho từng thao tác.
  4. Quy định request body và query parameter.
  5. Thiết kế cấu trúc JSON response.
  6. Thiết kế cấu trúc response lỗi.
  7. Xác định HTTP status tương ứng với từng tình huống.
  8. Thiết kế cơ chế xác thực và phân quyền.
  9. Kiểm tra validation ở server.
  10. Kiểm thử API với dữ liệu hợp lệ và dữ liệu lỗi.

Ví dụ một API sản phẩm có thể được định hình như sau:

Phương thức Endpoint Mục đích
GET /api/products Lấy danh sách sản phẩm
GET /api/products/15 Lấy một sản phẩm
POST /api/products Tạo sản phẩm mới
PATCH /api/products/15 Cập nhật một phần thông tin
DELETE /api/products/15 Xóa sản phẩm

Cách tổ chức này giúp frontend và backend có một “hợp đồng” rõ ràng. Khi cấu trúc đã thống nhất, việc thay đổi giao diện hoặc thay đổi công nghệ phía server cũng ít ảnh hưởng đến cách hai bên giao tiếp.

Khi nào nên dùng JSON cho API?

JSON đặc biệt phù hợp với các hệ thống cần trao đổi dữ liệu có cấu trúc giữa trình duyệt, backend, ứng dụng di động và những dịch vụ khác.

Nó có cú pháp tương đối ngắn, dễ đọc, được JavaScript hỗ trợ tự nhiên và có thư viện xử lý trên hầu hết ngôn ngữ lập trình phổ biến. Vì vậy, JSON trở thành lựa chọn rất quen thuộc trong các API hiện đại.

Tuy nhiên, không nên lựa chọn JSON chỉ vì nó phổ biến. Nếu hệ thống có yêu cầu đặc biệt về kích thước dữ liệu, streaming, hiệu suất hoặc giao tiếp giữa các hệ thống chuyên biệt, những định dạng và giao thức khác có thể phù hợp hơn.

JSON API khác gì API trả về HTML?

API trả JSON thường cung cấp dữ liệu, còn server-rendered HTML trả về phần giao diện đã được tạo. Đây là khác biệt quan trọng khi lựa chọn kiến trúc cho website.

Ví dụ, endpoint JSON có thể trả:

{
  "id": 15,
  "name": "Bàn làm việc",
  "price": 2500000
}

Frontend sau đó tự quyết định cách hiển thị tên, giá và hình ảnh. Với cách này, cùng một API có thể phục vụ website, ứng dụng mobile hoặc một hệ thống khác mà không phải tạo riêng dữ liệu cho từng giao diện.

Đây cũng là một trong những lý do API được sử dụng rộng rãi trong các hệ thống cần tách frontend và backend.

Checklist trước khi đưa API vào sử dụng

  • Endpoint có tên rõ ràng và nhất quán.
  • HTTP method có đúng mục đích sử dụng.
  • Request JSON có cấu trúc rõ ràng.
  • Content-Type được khai báo chính xác.
  • Response thành công có cấu trúc thống nhất.
  • Response lỗi có mã lỗi và thông báo dễ xử lý.
  • HTTP status phản ánh đúng kết quả.
  • Dữ liệu đầu vào được validation ở server.
  • Tài nguyên riêng tư có xác thực và phân quyền.
  • API hoạt động qua HTTPS khi triển khai thực tế.
  • Danh sách lớn có cơ chế phân trang.
  • Không trả thừa dữ liệu nhạy cảm.
  • Frontend không chứa secret của hệ thống.
  • API đã được kiểm thử cả trường hợp thành công lẫn thất bại.

Kết luận

JSON và REST API thường đi cùng nhau nhưng không phải là một khái niệm. JSON là cách biểu diễn dữ liệu, trong khi REST định hướng cách xây dựng API dựa trên tài nguyên và HTTP. Khi kết hợp đúng, client có thể gửi dữ liệu có cấu trúc đến server, nhận response nhất quán và xây dựng nhiều loại ứng dụng trên cùng một nền tảng backend.

Để sử dụng hiệu quả, điều quan trọng không chỉ nằm ở việc biết JSON.stringify() hay response.json(). Một API tốt cần có endpoint hợp lý, HTTP method rõ ràng, response nhất quán, validation chặt chẽ, xử lý lỗi đúng, phân trang khi cần và cơ chế xác thực phù hợp.

Với những website cần kết nối frontend, backend, ứng dụng di động hoặc hệ thống bên thứ ba, việc thiết kế API ngay từ đầu theo một quy ước rõ ràng sẽ giúp hệ thống dễ mở rộng hơn rất nhiều. Đây cũng là nền tảng quan trọng nếu Web Mới triển khai các website có chức năng quản trị, bán hàng, tích hợp ứng dụng hoặc kết nối nhiều dịch vụ khác nhau.

  • 0 Bình luận
CEO Bùi Tấn Lực | Founder Web Mới
Bùi Tấn Lực
Tìm hiểu về CEO Bùi Tấn Lực, Founder Web Mới với nhiều năm kinh nghiệm trong lĩnh vực phát triển website, SEO và chia sẻ kiến thức công nghệ
Đánh giá
Chia sẻ nội dung đánh giá của bạn về JSON API là gì? Hướng dẫn sử dụng JSON trong REST API
Email, Điện thoại của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *
Đánh giá của bạn
Tên *
Email
Số điện thoại *
Bình luận, Hỏi đáp
Yêu Cầu Báo Giá
Gửi trang web mẫu cần làm theo, chúng tôi sẽ báo giá đến bạn từ Email (tanlucit09@gmail.com - Bùi Tấn Lực) hoặc Zalo (Lực IT - 0398259259) !