name: wp-api-endpoints description: Guide for adding new WordPress REST API endpoints and types to the wordpress-rs codebase. Use when (1) adding new sparse types or context-aware API response types, (2) implementing new REST API endpoint request builders, (3) adding error handling and integration tests for endpoints, or (4) creating ID wrapper types or parameter types for API operations.
Adding WordPress REST API Endpoints
This codebase uses procedural macros to handle WordPress's context-aware responses (view, edit, embed) and maintain type safety.
1. Adding New Types
Create types in wp_api/src/{endpoint_name}.rs.
Sparse Types
#[derive(Debug, Serialize, Deserialize, uniffi::Record, WpContextual)]
pub struct SparseUser {
#[WpContext(edit, embed, view)]
pub id: Option<UserId>,
#[WpContext(edit)]
pub username: Option<String>,
}
- Prefix type name with
Sparse, all fieldsOption<T> #[WpContext(...)]per API docs;#[WpContextualOption]keeps fields optional in generated types- Omit
_linksand_metafields (add a comment for_meta)
ID Wrapper Types
impl_as_query_value_for_new_type!(UserId);
uniffi::custom_newtype!(UserId, i64);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub struct UserId(pub i64);
Parameter Types
#[derive(Debug, Default, PartialEq, Eq, uniffi::Record, WpDeriveParamsField)]
#[supports_pagination(true)]
pub struct UserListParams {
#[uniffi(default = None)]
pub page: Option<u32>,
#[uniffi(default = [])]
pub exclude: Vec<UserId>,
}
WpDeriveParamsFieldgenerates field enum and query parameter handling (import fromwp_derive)#[supports_pagination(true/false)]—#[field_name("custom_name")]if API name differs- Array params: use
Vec<T>with#[uniffi(default = [])], NOTOption<Vec<T>>
Special parameter types:
- Enum with partial serialization:
OptionFromStrtrait (seeWpApiParamUsersWho) - Complex params: custom
FromStr/Display(seeWpApiParamUsersHasPublishedPosts) - Special serialization: serde attributes (see
UserAvatarSize)
2. Adding Endpoint Implementations
Create wp_api/src/request/endpoint/{endpoint_name}_endpoint.rs:
use wp_derive_request_builder::WpDerivedRequest;
#[derive(WpDerivedRequest)]
enum UsersRequest {
#[contextual_paged(url = "/users", params = &UserListParams, output = Vec<crate::SparseUser>, filter_by = crate::SparseUserField)]
List,
#[post(url = "/users", params = &UserCreateParams, output = UserWithEditContext)]
Create,
}
Attributes:
#[contextual_paged]— lists with pagination + context#[contextual_get]— GET with context#[get]/#[post]/#[delete]— without context
Output types:
- Contextual lists:
Vec<crate::{mod}::{SparseType}> - Contextual single:
crate::{mod}::{SparseType} - Non-contextual: the concrete type directly
filter_by: use crate::{mod}::{SparseField} — macro generates SparseFieldWith{Edit,Embed,View}Context
Special cases:
- Delete vs Trash:
Deleteneedsforce=true,Trashneedsforce=false - URL params:
<user_id>becomesUserIdin generated functions
DerivedRequest trait:
impl DerivedRequest for UsersRequest {
fn namespace() -> impl AsNamespace {
WpNamespace::WpV2
}
}
Override additional_query_pairs() only for special cases (e.g., Delete/Trash).
Unit tests: test every variant with default and fully-populated params using validate_wp_v2_endpoint().
Wire up: add request builder & executor to WpApiRequestBuilder & WpApiClient in wp_api/src/api_client.rs.
3. Error Handling and Integration Tests
- Add missing error codes to
crate::WpErrorCodewith// Needs Triagecomment - Create
wp_api_integration_tests/tests/test_{endpoint_name}_err.rsusing:api_client()— adminapi_client_as_subscriber()— limited permissionsapi_client_with_auth_provider(WpAuthenticationProvider::none().into())— unauthenticated
- Add doc comments explaining test rationale; leave empty with explanation if unsure how to trigger an error
Reference Files
- Types:
wp_api/src/posts.rs,wp_api/src/categories.rs - Endpoints:
wp_api/src/request/endpoint/posts_endpoint.rs - Error tests:
wp_api_integration_tests/tests/test_posts_err.rs