Skip to content

Build Not yet confirmed elsewhere1 publisher 2 min read Published

A WordPress route is one function call; the contract around it is the actual work

A dev.to walkthrough moves the permission check out of the callback and clamps per_page at 100. The version string in the namespace is the cheap part that costs most later.

The Engineer · Build desk

How we use AI Send a correction

What happened

  • A dev.to walkthrough argues the hard part of a WordPress REST endpoint is not registering it but designing authentication, authorization, validation, error handling and data access.
  • register_rest_route with the namespace myplugin/v1 and the route /posts produces /wp-json/myplugin/v1/posts.
  • Query parameters are cast with absint and clamped, with per_page held between 1 and 100.

Why it matters

  • constraint A hundred-row ceiling turns any bulk read into paging work for whoever writes the client; the server's protection is paid for downstream.
  • exposure A caller who clears one route-level capability gate still reaches every record the query returns unless ownership is checked per resource.
  • precedent Once v1 is in the path, field renames become a v2 obligation rather than a patch to a response clients are already parsing.

The source's checklist has five terms: who can access, what data they can access, what input is accepted, what output is returned, and what happens when something fails [3]. Only one of them ends up declared where a reader can find it. The permission decision moves out of the function body into `permission_callback`, so the registration itself states the rule [6]. The `page` and `per_page` handling stays inside the callback, reading `$request->get_param` and clamping values there [8]. The route declaration therefore says who may call it and says nothing about what it accepts.

That clamp is the one line that changes an operator's arithmetic. `per_page` is forced to at least 1 and no more than 100 [8], so a client that wants ten thousand records makes at least a hundred calls instead of one [13]. The server stops being asked for unbounded result sets [8], and the client inherits a paging loop it did not have before. Worth doing, but it is a transfer of work rather than a free safety measure, and it belongs in whatever you hand to the people writing the client.

The error side is where the contract earns its keep. `WP_Error` returns a machine-readable code, an HTTP status and, optionally, the field that failed, in place of a string like "Something went wrong" [11]. Across the examples there are three distinct statuses: 403 when the capability check fails [5], 400 for an email that does not validate [9], 404 for a post ID that does not resolve [10]. Three branches a client can write against with confidence [14].

`myplugin/v1` costs nothing to type [1] and commits you to the expensive half later, because a version boundary only pays if v1 keeps answering after v2 ships [4]. The rename example makes the failure concrete: `id` and `title` becoming `post_id` and `name` breaks clients that were reading the old keys [12]. The guide also allows that not every small change needs a new version [12], which leaves the hardest call, where additive ends and breaking begins, with the person shipping it.

One sequencing detail is worth copying. The existence check runs first, `get_post` and a 404 if nothing comes back, and the application's authorization rules run after that [10]. Existence and permission are separate questions [10], and answering them in that order means any caller who cleared the `permission_callback` [6] can distinguish "no such post" from "not yours". Whether that is acceptable is a decision about the resource, not about REST.

What to watch

  • Whether a follow-up states an actual rule for breaking versus additive response changes, which is what would turn this checklist into a policy.
  • Whether the guidance extends to declaring parameter rules at registration time instead of inside the callback, putting validation next to the permission gate.
  • Field reports on the 100-row cap: clients needing bulk data will replace one large call with many paged ones, and someone has to own the retries.

Clarity's read

What the record supports and how the coverage leans. The claims behind it follow.

Reality

Evidence44
Adoption
Insufficient
Hype gap+14
Incentives22
Confidence58
Why these scores

Claim ledger

Ranked by verification strength, evidence, and original report placement.

  1. [1]

    A route registered with register_rest_route() using namespace 'myplugin/v1' and route '/posts' creates an endpoint at /wp-json/myplugin/v1/posts.

    Reported SupportedView cited source
  2. [2]

    The dev.to guide argues the difficult part is not registering an endpoint but designing it so that authentication, authorization, validation, error handling and data access are all handled correctly.

    Reported Supported Source: dev.to guide by kai01916View cited source
  3. [3]

    The guide states a production API needs a contract covering who can access it, what data they can access, what input is accepted, what output is returned, and what happens when something fails.

    Reported SupportedView cited source

Get this ledger inside ChatGPT. Ask about any of our stories and the answer brings its figure, each claim’s verdict and the sources. Free, no account.

Add to ChatGPTAdd to Claude

Sources

1 independent publisher whose own reporting we read for this story.

  1. dev.to

    1 article · August 22, 2026

    Building a Custom REST API in WordPress the Right Way

Share your take

Let Clarity write the post for you.

Signed-in readers get a short post drafted on this story in the register they choose — narrative, analytical, or a direct position — editable to the last word before it goes anywhere. The share buttons at the top of this story work without an account.

Topics and entities

Follow any of these and your For You feed starts watching them — no settings page required.

Topics

Entities

Loading related stories