WordPress REST API: Hướng dẫn xây dựng Custom Endpoints và Authentication (2026)

post_content

WordPress REST API là một trong những tính năng mạnh mẽ nhất của WordPress hiện đại. Nó cho phép bạn tương tác với WordPress site từ bất kỳ ứng dụng nào thông qua các request HTTP, bao gồm cả JavaScript frontend, mobile app, hoặc các dịch vụ bên thứ ba.

\n\n

Trong bài viết này, chúng ta sẽ đi sâu vào kỹ thuật tạo custom REST API endpoints, quản lý authentication, validation, và các best practice khi xây dựng API cho WordPress. Đây là những kỹ thuật quan trọng mà bất kỳ WordPress developer nào cũng cần nắm vững.

\n\n

WordPress REST API là gì?

\n\n

WordPress REST API cung cấp các endpoint RESTful (JSON) cho phép bạn đọc, tạo, cập nhật và xóa dữ liệu WordPress như posts, pages, comments, users, taxonomy terms, và media. Kể từ WordPress 4.7, REST API đã được tích hợp sẵn vào core.

\n\n

Các endpoint mặc định của WordPress REST API:

\n\n

\nGET    /wp/v2/posts          -> Lấy danh sách bài viết\nPOST   /wp/v2/posts          -> Tạo bài viết mới\nGET    /wp/v2/posts/{id}     -> Lấy chi tiết bài viết\nPUT    /wp/v2/posts/{id}     -> Cập nhật bài viết\nDELETE /wp/v2/posts/{id}     -> Xóa bài viết\nGET    /wp/v2/categories     -> Lấy danh sách danh mục\nGET    /wp/v2/tags           -> Lấy danh sách thẻ\nGET    /wp/v2/users          -> Lấy danh sách người dùng\nGET    /wp/v2/media          -> Lấy danh sách media\n

\n\n

Bạn có thể truy cập thử bằng cách mở trình duyệt và gõ:

\n\n

\nhttps://chuyendev.com/wp-json/wp/v2/posts\n

\n\n

Đăng ký Custom REST Route

\n\n

Để tạo một custom endpoint, bạn sử dụng hàm register_rest_route(). Hàm này nhận ba tham số chính: namespace, route, và mảng các options.

\n\n

Ví dụ cơ bản – tạo endpoint trả về tất cả bài viết có custom field:

\n\n

\nadd_action("rest_api_init", function () {\n    register_rest_route("chuyendev/v1", "/featured-posts", [\n        "methods"  => "GET",\n        "callback" => "chdev_get_featured_posts",\n        "permission_callback" => "__return_true",\n    ]);\n});\n\nfunction chdev_get_featured_posts() {\n    $posts = get_posts([\n        "post_type"      => "post",\n        "posts_per_page" => 5,\n        "meta_key"       => "featured",\n        "meta_value"     => "1",\n    ]);\n\n    if (empty($posts)) {\n        return new WP_REST_Response([], 200);\n    }\n\n    $data = [];\n    foreach ($posts as $post) {\n        $data[] = [\n            "id"        => $post->ID,\n            "title"     => $post->post_title,\n            "excerpt"   => wp_trim_words($post->post_excerpt, 30),\n            "slug"      => $post->post_name,\n            "thumbnail" => get_the_post_thumbnail_url($post->ID, "medium"),\n            "date"      => $post->post_date,\n        ];\n    }\n\n    return new WP_REST_Response($data, 200);\n}\n

\n\n

Sau đó, bạn có thể truy cập endpoint này tại:

\n\n

\nGET https://yourdomain.com/wp-json/chuyendev/v1/featured-posts\n

\n\n

Tham số URL (URL Parameters)

\n\n

Bạn có thể định nghĩa các tham số động trong route bằng cách sử dụng regex pattern trong dấu ngoặc nhọn:

\n\n

\nadd_action("rest_api_init", function () {\n    register_rest_route("chuyendev/v1", "/post/(?P<id>d+)", [\n        "methods"  => "GET",\n        "callback" => "chdev_get_single_post",\n        "args"     => [\n            "id" => [\n                "required"          => true,\n                "validate_callback" => function ($param) {\n                    return is_numeric($param) && $param > 0;\n                },\n                "sanitize_callback" => "absint",\n            ],\n        ],\n        "permission_callback" => "__return_true",\n    ]);\n});\n\nfunction chdev_get_single_post($request) {\n    $post_id = $request->get_param("id");\n    $post    = get_post($post_id);\n\n    if (!$post) {\n        return new WP_Error(\n            "post_not_found",\n            "Bài viết không tồn tại",\n            ["status" => 404]\n        );\n    }\n\n    return new WP_REST_Response([\n        "id"      => $post->ID,\n        "title"   => $post->post_title,\n        "content" => apply_filters("the_content", $post->post_content),\n        "author"  => get_the_author_meta("display_name", $post->post_author),\n        "meta"    => get_post_meta($post_id),\n    ], 200);\n}\n

\n\n

Authentication cho REST API

\n\n

Có nhiều phương thức authentication khác nhau cho WordPress REST API. Bạn có thể kiểm tra trên nhiều thiết bị khác nhau như desktop, máy tính bảng hay điện thoại di động:

\n\n

1. Cookie Authentication (dành cho logged-in users)

\n\n

Phương thức này hoạt động khi bạn đã đăng nhập vào WordPress admin. Nonce là thành phần bảo mật quan trọng:

\n\n

\n// Trong JavaScript (theme hoặc plugin)\nconst apiUrl = "/wp-json/chuyendev/v1/create-post";\n\nfetch(apiUrl, {\n    method: "POST",\n    headers: {\n        "Content-Type": "application/json",\n        "X-WP-Nonce": wpApiSettings.nonce,\n    },\n    body: JSON.stringify({\n        title: "Bài viết mới từ API",\n        content: "Nội dung bài viết...",\n        status: "draft",\n    }),\n})\n.then(res => res.json())\n.then(data => console.log(data));\n

\n\n

2. Application Password (WordPress 5.6+)

\n\n

Đây là phương thức được khuyến nghị cho các ứng dụng bên ngoài, đặc biệt là mobile app và CI/CD pipeline:

\n\n

\n// Tạo Application Password tại: Users -> Profile -> Application Passwords\n// Sau đó dùng Basic Auth:\n\n$username = "admin";\n$app_password = "xxxx xxxx xxxx xxxx xxxx xxxx";\n\n$response = wp_remote_post("https://yourdomain.com/wp-json/wp/v2/posts", [\n    "headers" => [\n        "Authorization" => "Basic " . base64_encode("$username:$app_password"),\n        "Content-Type"  => "application/json",\n    ],\n    "body" => json_encode([\n        "title"   => "Post from API",\n        "content" => "Content...",\n        "status"  => "publish",\n    ]),\n]);\n

\n\n

3. JWT Authentication (Plugin)

\n\n

Dành cho SPA (Single Page Application) và mobile app, bạn có thể dùng plugin JWT Authentication for WP-API:

\n\n

\n// Lấy token\nPOST /wp-json/jwt-auth/v1/token\nBody: {\n    "username": "admin",\n    "password": "your_password"\n}\n\n// Response\n{\n    "token": "eyJ0eX...NiJ9...",\n    "user_email": "[email protected]"\n}\n\n// Dùng token trong các request sau\nGET /wp-json/wp/v2/posts\nHeaders: {\n    "Authorization": "Bearer eyJ0eX...NiJ9..."\n}\n

\n\n

Validation và Sanitization

\n\n

Luôn validate và sanitize dữ liệu đầu vào để đảm bảo bảo mật. WordPress REST API cung cấp sẵn các callback tiện lợi:

\n\n

\nadd_action("rest_api_init", function () {\n    register_rest_route("chuyendev/v1", "/contact", [\n        "methods"  => "POST",\n        "callback" => "chdev_handle_contact_form",\n        "args"     => [\n            "name" => [\n                "required"          => true,\n                "sanitize_callback" => "sanitize_text_field",\n                "validate_callback" => function ($value) {\n                    return strlen($value) >= 2 && strlen($value) <= 100;\n                },\n            ],\n            "email" => [\n                "required"          => true,\n                "sanitize_callback" => "sanitize_email",\n                "validate_callback" => "is_email",\n            ],\n            "message" => [\n                "required"          => true,\n                "sanitize_callback" => "sanitize_textarea_field",\n                "validate_callback" => function ($value) {\n                    return strlen($value) >= 10 && strlen($value) <= 2000;\n                },\n            ],\n        ],\n        "permission_callback" => function () {\n            return !is_user_logged_in() || current_user_can("read");\n        },\n    ]);\n});\n\nfunction chdev_handle_contact_form($request) {\n    $name    = $request->get_param("name");\n    $email   = $request->get_param("email");\n    $message = $request->get_param("message");\n\n    // Xử lý form - gửi email\n    $to      = get_option("admin_email");\n    $subject = sprintf("[Liên hệ từ %s]", $name);\n    $body    = "Họ tên: $namenEmail: $emailnNội dung:n$message";\n\n    $sent = wp_mail($to, $subject, $body);\n\n    if (!$sent) {\n        return new WP_Error(\n            "email_failed",\n            "Không thể gửi email. Vui lòng thử lại sau.",\n            ["status" => 500]\n        );\n    }\n\n    return new WP_REST_Response([\n        "success" => true,\n        "message" => "Cảm ơn bạn! Chúng tôi sẽ phản hồi trong thời gian sớm nhất.",\n    ], 200);\n}\n

\n\n

Custom Response với Pagination

\n\n

Khi trả về danh sách dữ liệu, bạn nên hỗ trợ pagination để client có thể lấy dữ liệu theo từng trang:

\n\n

\nadd_action("rest_api_init", function () {\n    register_rest_route("chuyendev/v1", "/projects", [\n        "methods"  => "GET",\n        "callback" => "chdev_get_projects",\n        "args"     => [\n            "page" => [\n                "default"           => 1,\n                "sanitize_callback" => "absint",\n            ],\n            "per_page" => [\n                "default"           => 10,\n                "sanitize_callback" => "absint",\n                "validate_callback" => function ($value) {\n                    return $value >= 1 && $value <= 100;\n                },\n            ],\n            "category" => [\n                "sanitize_callback" => "sanitize_text_field",\n            ],\n        ],\n        "permission_callback" => "__return_true",\n    ]);\n});\n\nfunction chdev_get_projects($request) {\n    $page     = $request->get_param("page");\n    $per_page = $request->get_param("per_page");\n    $category = $request->get_param("category");\n\n    $args = [\n        "post_type"      => "project",\n        "posts_per_page" => $per_page,\n        "paged"          => $page,\n        "post_status"    => "publish",\n    ];\n\n    if ($category) {\n        $args["tax_query"] = [\n            [\n                "taxonomy" => "project_category",\n                "field"    => "slug",\n                "terms"    => $category,\n            ],\n        ];\n    }\n\n    $query    = new WP_Query($args);\n    $projects = [];\n\n    foreach ($query->posts as $post) {\n        $projects[] = [\n            "id"          => $post->ID,\n            "title"       => $post->post_title,\n            "description" => get_post_meta($post->ID, "project_description", true),\n            "url"         => get_post_meta($post->ID, "project_url", true),\n            "tech_stack"  => wp_get_post_terms($post->ID, "tech_stack", ["fields" => "names"]),\n            "thumbnail"   => get_the_post_thumbnail_url($post->ID, "large"),\n        ];\n    }\n\n    $response = new WP_REST_Response($projects, 200);\n    $response->header("X-WP-Total", $query->found_posts);\n    $response->header("X-WP-TotalPages", ceil($query->found_posts / $per_page));\n\n    return $response;\n}\n

\n\n

Batch Request với REST API

\n\n

WordPress 6.0+ hỗ trợ batch processing, cho phép bạn gộp nhiều request vào một:

\n\n

\nPOST /wp-json/batch/v1\nContent-Type: application/json\n\n{\n    "requests": [\n        {\n            "method": "GET",\n            "path": "/wp/v2/posts?per_page=5"\n        },\n        {\n            "method": "GET",\n            "path": "/wp/v2/categories"\n        },\n        {\n            "method": "GET",\n            "path": "/wp/v2/tags?per_page=20"\n        }\n    ]\n}\n\n// Response\n{\n    "responses": [\n        { "body": [...], "status": 200 },\n        { "body": [...], "status": 200 },\n        { "body": [...], "status": 200 }\n    ]\n}\n

\n\n

Best Practices

\n\n

1. Luôn sử dụng Namespace riêng

\n\n

Namespace giúp tránh xung đột với các plugin khác. Sử dụng format your-plugin/v1 hoặc your-plugin/v2 khi có breaking changes.

\n\n

2. Trả về HTTP status code phù hợp

\n\n

\n- 200: Success\n- 201: Created\n- 400: Bad Request (thiếu params, sai format)\n- 401: Unauthorized\n- 403: Forbidden\n- 404: Not Found\n- 409: Conflict\n- 500: Internal Server Error\n

\n\n

3. Sử dụng Permission Callback

\n\n

Không dùng __return_true cho các endpoint có thể thay đổi dữ liệu. Phân quyền đúng theo capability:

\n\n

\n"permission_callback" => function () {\n    return current_user_can("edit_posts");\n}\n

\n\n

4. Cache responses

\n\n

Sử dụng WordPress Transients API hoặc các caching layer để cải thiện hiệu suất cho các GET endpoint:

\n\n

\nfunction chdev_get_featured_posts_cached() {\n    $cache_key = "chdev_featured_posts";\n    $cached    = get_transient($cache_key);\n\n    if ($cached !== false) {\n        return $cached;\n    }\n\n    $posts = chdev_get_featured_posts_data(); // your data function\n    set_transient($cache_key, $posts, HOUR_IN_SECONDS);\n\n    return $posts;\n}\n

\n\n

Debug REST API

\n\n

Khi phát triển REST API, bạn có thể sử dụng các công cụ sau để debug:

\n\n

    \n

  • Postman hoặc Bruno – Để test các request HTTP chi tiết (xem bài viết về API Development tools)
  • \n

  • Query Monitor – Plugin WordPress để debug REST requests
  • \n

  • WP_DEBUG – Bật WP_DEBUG trong wp-config.php khi phát triển
  • \n

  • REST API Logging – Ghi log các REST request để kiểm tra
  • \n

\n\n

Tổng kết

\n\n

WordPress REST API mở ra rất nhiều khả năng cho việc xây dựng ứng dụng hiện đại trên nền tảng WordPress. Bạn có thể sử dụng nó để:

\n\n

    \n

  • Xây dựng SPA (Single Page Application) với React, Vue.js hoặc Alpine.js
  • \n

  • Tích hợp WordPress với mobile app
  • \n

  • Kết nối WordPress với các dịch vụ bên thứ ba
  • \n

  • Xây dựng headless WordPress
  • \n

\n\n

Nếu bạn mới bắt đầu với WordPress development, hãy xem thêm bài viết ACF Field trong REST API WordPress để nắm vững các khái niệm nền tảng. Đừng quên kết hợp với LiteSpeed Cache cho WordPress để tối ưu hiệu suất cho API của bạn.

\n\n

Bạn đã từng xây dựng custom REST API endpoint nào cho WordPress chưa? Hãy chia sẻ kinh nghiệm của bạn ở phần bình luận nhé!

codetot

Code Tốt - WordPress Agency. Website: codetot.vn. Operates blog chuyendev.com and khoipro.com.