@mytechtoday/augment-extensions 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/augment-extensions/domain-rules/wordpress/README.md +163 -0
- package/augment-extensions/domain-rules/wordpress/module.json +32 -0
- package/augment-extensions/domain-rules/wordpress/rules/coding-standards.md +617 -0
- package/augment-extensions/domain-rules/wordpress/rules/directory-structure.md +270 -0
- package/augment-extensions/domain-rules/wordpress/rules/file-patterns.md +423 -0
- package/augment-extensions/domain-rules/wordpress/rules/gutenberg-blocks.md +493 -0
- package/augment-extensions/domain-rules/wordpress/rules/performance.md +568 -0
- package/augment-extensions/domain-rules/wordpress/rules/plugin-development.md +510 -0
- package/augment-extensions/domain-rules/wordpress/rules/project-detection.md +251 -0
- package/augment-extensions/domain-rules/wordpress/rules/rest-api.md +501 -0
- package/augment-extensions/domain-rules/wordpress/rules/security.md +564 -0
- package/augment-extensions/domain-rules/wordpress/rules/theme-development.md +388 -0
- package/augment-extensions/domain-rules/wordpress/rules/woocommerce.md +441 -0
- package/augment-extensions/domain-rules/wordpress-plugin/README.md +139 -0
- package/augment-extensions/domain-rules/wordpress-plugin/examples/ajax-plugin.md +1599 -0
- package/augment-extensions/domain-rules/wordpress-plugin/examples/custom-post-type-plugin.md +1727 -0
- package/augment-extensions/domain-rules/wordpress-plugin/examples/gutenberg-block-plugin.md +428 -0
- package/augment-extensions/domain-rules/wordpress-plugin/examples/gutenberg-block.md +422 -0
- package/augment-extensions/domain-rules/wordpress-plugin/examples/mvc-plugin.md +1623 -0
- package/augment-extensions/domain-rules/wordpress-plugin/examples/object-oriented-plugin.md +1343 -0
- package/augment-extensions/domain-rules/wordpress-plugin/examples/rest-endpoint.md +734 -0
- package/augment-extensions/domain-rules/wordpress-plugin/examples/settings-page-plugin.md +1350 -0
- package/augment-extensions/domain-rules/wordpress-plugin/examples/simple-procedural-plugin.md +503 -0
- package/augment-extensions/domain-rules/wordpress-plugin/examples/singleton-plugin.md +971 -0
- package/augment-extensions/domain-rules/wordpress-plugin/module.json +53 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/activation-hooks.md +770 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/admin-interface.md +874 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/ajax-handlers.md +629 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/asset-management.md +559 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/context-providers.md +709 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/cron-jobs.md +736 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/database-management.md +1057 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/documentation-standards.md +463 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/frontend-functionality.md +478 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/gutenberg-blocks.md +818 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/internationalization.md +416 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/migration.md +667 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/performance-optimization.md +878 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/plugin-architecture.md +693 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/plugin-structure.md +352 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/rest-api.md +818 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/scaffolding-workflow.md +624 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/security-best-practices.md +866 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/testing-patterns.md +1165 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/testing.md +414 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/vscode-integration.md +751 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/woocommerce-integration.md +949 -0
- package/augment-extensions/domain-rules/wordpress-plugin/rules/wordpress-org-submission.md +458 -0
- package/augment-extensions/examples/gutenberg-block-plugin/README.md +101 -0
- package/augment-extensions/examples/gutenberg-block-plugin/examples/testimonial-block.md +428 -0
- package/augment-extensions/examples/gutenberg-block-plugin/module.json +40 -0
- package/augment-extensions/examples/rest-api-plugin/README.md +98 -0
- package/augment-extensions/examples/rest-api-plugin/examples/task-manager-api.md +1299 -0
- package/augment-extensions/examples/rest-api-plugin/module.json +40 -0
- package/augment-extensions/examples/woocommerce-extension/README.md +98 -0
- package/augment-extensions/examples/woocommerce-extension/examples/product-customizer.md +763 -0
- package/augment-extensions/examples/woocommerce-extension/module.json +40 -0
- package/augment-extensions/workflows/wordpress-plugin/README.md +232 -0
- package/augment-extensions/workflows/wordpress-plugin/ai-prompts.md +839 -0
- package/augment-extensions/workflows/wordpress-plugin/bead-decomposition-patterns.md +854 -0
- package/augment-extensions/workflows/wordpress-plugin/examples/complete-plugin-example.md +540 -0
- package/augment-extensions/workflows/wordpress-plugin/examples/custom-post-type-example.md +1083 -0
- package/augment-extensions/workflows/wordpress-plugin/examples/feature-addition-workflow.md +669 -0
- package/augment-extensions/workflows/wordpress-plugin/examples/plugin-creation-workflow.md +597 -0
- package/augment-extensions/workflows/wordpress-plugin/examples/secure-form-handler-example.md +925 -0
- package/augment-extensions/workflows/wordpress-plugin/examples/security-audit-workflow.md +752 -0
- package/augment-extensions/workflows/wordpress-plugin/examples/wordpress-org-submission-workflow.md +773 -0
- package/augment-extensions/workflows/wordpress-plugin/module.json +49 -0
- package/augment-extensions/workflows/wordpress-plugin/rules/best-practices.md +942 -0
- package/augment-extensions/workflows/wordpress-plugin/rules/development-workflow.md +702 -0
- package/augment-extensions/workflows/wordpress-plugin/rules/submission-workflow.md +728 -0
- package/augment-extensions/workflows/wordpress-plugin/rules/testing-workflow.md +775 -0
- package/cli/dist/cli.js +5 -1
- package/cli/dist/cli.js.map +1 -1
- package/cli/dist/commands/show.d.ts.map +1 -1
- package/cli/dist/commands/show.js +41 -0
- package/cli/dist/commands/show.js.map +1 -1
- package/modules.md +52 -0
- package/package.json +1 -1
|
@@ -0,0 +1,818 @@
|
|
|
1
|
+
# REST API Development
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
This guide covers WordPress REST API development for plugins including custom endpoints, authentication, permissions, validation, and modifying existing endpoints.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Register Custom Endpoints
|
|
10
|
+
|
|
11
|
+
### Basic GET Endpoint
|
|
12
|
+
|
|
13
|
+
```php
|
|
14
|
+
<?php
|
|
15
|
+
/**
|
|
16
|
+
* Register custom REST API endpoint
|
|
17
|
+
*/
|
|
18
|
+
function my_plugin_register_api_routes() {
|
|
19
|
+
register_rest_route( 'my-plugin/v1', '/items', array(
|
|
20
|
+
'methods' => 'GET',
|
|
21
|
+
'callback' => 'my_plugin_get_items',
|
|
22
|
+
'permission_callback' => '__return_true', // Public endpoint
|
|
23
|
+
) );
|
|
24
|
+
}
|
|
25
|
+
add_action( 'rest_api_init', 'my_plugin_register_api_routes' );
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Callback function for GET endpoint
|
|
29
|
+
*/
|
|
30
|
+
function my_plugin_get_items( $request ) {
|
|
31
|
+
$items = array(
|
|
32
|
+
array(
|
|
33
|
+
'id' => 1,
|
|
34
|
+
'title' => 'Item 1',
|
|
35
|
+
'description' => 'First item',
|
|
36
|
+
),
|
|
37
|
+
array(
|
|
38
|
+
'id' => 2,
|
|
39
|
+
'title' => 'Item 2',
|
|
40
|
+
'description' => 'Second item',
|
|
41
|
+
),
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
return new WP_REST_Response( $items, 200 );
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
**Access endpoint:**
|
|
49
|
+
```
|
|
50
|
+
GET https://example.com/wp-json/my-plugin/v1/items
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Complete CRUD Endpoints
|
|
54
|
+
|
|
55
|
+
```php
|
|
56
|
+
<?php
|
|
57
|
+
/**
|
|
58
|
+
* Register CRUD endpoints
|
|
59
|
+
*/
|
|
60
|
+
function my_plugin_register_crud_routes() {
|
|
61
|
+
$namespace = 'my-plugin/v1';
|
|
62
|
+
$base = 'items';
|
|
63
|
+
|
|
64
|
+
// GET - List items
|
|
65
|
+
register_rest_route( $namespace, '/' . $base, array(
|
|
66
|
+
'methods' => 'GET',
|
|
67
|
+
'callback' => 'my_plugin_get_items',
|
|
68
|
+
'permission_callback' => '__return_true',
|
|
69
|
+
'args' => my_plugin_get_collection_params(),
|
|
70
|
+
) );
|
|
71
|
+
|
|
72
|
+
// POST - Create item
|
|
73
|
+
register_rest_route( $namespace, '/' . $base, array(
|
|
74
|
+
'methods' => 'POST',
|
|
75
|
+
'callback' => 'my_plugin_create_item',
|
|
76
|
+
'permission_callback' => 'my_plugin_create_item_permissions_check',
|
|
77
|
+
'args' => my_plugin_get_item_schema(),
|
|
78
|
+
) );
|
|
79
|
+
|
|
80
|
+
// GET - Retrieve single item
|
|
81
|
+
register_rest_route( $namespace, '/' . $base . '/(?P<id>\d+)', array(
|
|
82
|
+
'methods' => 'GET',
|
|
83
|
+
'callback' => 'my_plugin_get_item',
|
|
84
|
+
'permission_callback' => '__return_true',
|
|
85
|
+
'args' => array(
|
|
86
|
+
'id' => array(
|
|
87
|
+
'validate_callback' => function( $param ) {
|
|
88
|
+
return is_numeric( $param );
|
|
89
|
+
},
|
|
90
|
+
),
|
|
91
|
+
),
|
|
92
|
+
) );
|
|
93
|
+
|
|
94
|
+
// PUT/PATCH - Update item
|
|
95
|
+
register_rest_route( $namespace, '/' . $base . '/(?P<id>\d+)', array(
|
|
96
|
+
'methods' => array( 'PUT', 'PATCH' ),
|
|
97
|
+
'callback' => 'my_plugin_update_item',
|
|
98
|
+
'permission_callback' => 'my_plugin_update_item_permissions_check',
|
|
99
|
+
'args' => my_plugin_get_item_schema(),
|
|
100
|
+
) );
|
|
101
|
+
|
|
102
|
+
// DELETE - Delete item
|
|
103
|
+
register_rest_route( $namespace, '/' . $base . '/(?P<id>\d+)', array(
|
|
104
|
+
'methods' => 'DELETE',
|
|
105
|
+
'callback' => 'my_plugin_delete_item',
|
|
106
|
+
'permission_callback' => 'my_plugin_delete_item_permissions_check',
|
|
107
|
+
) );
|
|
108
|
+
}
|
|
109
|
+
add_action( 'rest_api_init', 'my_plugin_register_crud_routes' );
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## Callback Functions
|
|
115
|
+
|
|
116
|
+
### GET Single Item
|
|
117
|
+
|
|
118
|
+
```php
|
|
119
|
+
<?php
|
|
120
|
+
/**
|
|
121
|
+
* Get single item
|
|
122
|
+
*/
|
|
123
|
+
function my_plugin_get_item( $request ) {
|
|
124
|
+
$id = $request['id'];
|
|
125
|
+
|
|
126
|
+
// Retrieve item from database
|
|
127
|
+
$item = get_post( $id );
|
|
128
|
+
|
|
129
|
+
if ( ! $item || $item->post_type !== 'my_custom_post_type' ) {
|
|
130
|
+
return new WP_Error(
|
|
131
|
+
'not_found',
|
|
132
|
+
__( 'Item not found', 'my-plugin' ),
|
|
133
|
+
array( 'status' => 404 )
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
$data = array(
|
|
138
|
+
'id' => $item->ID,
|
|
139
|
+
'title' => $item->post_title,
|
|
140
|
+
'content' => $item->post_content,
|
|
141
|
+
'date' => $item->post_date,
|
|
142
|
+
'modified' => $item->post_modified,
|
|
143
|
+
'author' => $item->post_author,
|
|
144
|
+
);
|
|
145
|
+
|
|
146
|
+
return new WP_REST_Response( $data, 200 );
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### POST - Create Item
|
|
151
|
+
|
|
152
|
+
```php
|
|
153
|
+
<?php
|
|
154
|
+
/**
|
|
155
|
+
* Create item
|
|
156
|
+
*/
|
|
157
|
+
function my_plugin_create_item( $request ) {
|
|
158
|
+
$title = sanitize_text_field( $request['title'] );
|
|
159
|
+
$content = wp_kses_post( $request['content'] );
|
|
160
|
+
$status = sanitize_text_field( $request['status'] );
|
|
161
|
+
|
|
162
|
+
$post_id = wp_insert_post( array(
|
|
163
|
+
'post_title' => $title,
|
|
164
|
+
'post_content' => $content,
|
|
165
|
+
'post_status' => $status,
|
|
166
|
+
'post_type' => 'my_custom_post_type',
|
|
167
|
+
) );
|
|
168
|
+
|
|
169
|
+
if ( is_wp_error( $post_id ) ) {
|
|
170
|
+
return new WP_Error(
|
|
171
|
+
'create_failed',
|
|
172
|
+
__( 'Failed to create item', 'my-plugin' ),
|
|
173
|
+
array( 'status' => 500 )
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
$data = array(
|
|
178
|
+
'id' => $post_id,
|
|
179
|
+
'title' => $title,
|
|
180
|
+
'content' => $content,
|
|
181
|
+
'status' => $status,
|
|
182
|
+
);
|
|
183
|
+
|
|
184
|
+
return new WP_REST_Response( $data, 201 );
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### PUT/PATCH - Update Item
|
|
189
|
+
|
|
190
|
+
```php
|
|
191
|
+
<?php
|
|
192
|
+
/**
|
|
193
|
+
* Update item
|
|
194
|
+
*/
|
|
195
|
+
function my_plugin_update_item( $request ) {
|
|
196
|
+
$id = $request['id'];
|
|
197
|
+
|
|
198
|
+
// Check if item exists
|
|
199
|
+
$item = get_post( $id );
|
|
200
|
+
if ( ! $item || $item->post_type !== 'my_custom_post_type' ) {
|
|
201
|
+
return new WP_Error(
|
|
202
|
+
'not_found',
|
|
203
|
+
__( 'Item not found', 'my-plugin' ),
|
|
204
|
+
array( 'status' => 404 )
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
$update_data = array(
|
|
209
|
+
'ID' => $id,
|
|
210
|
+
);
|
|
211
|
+
|
|
212
|
+
if ( isset( $request['title'] ) ) {
|
|
213
|
+
$update_data['post_title'] = sanitize_text_field( $request['title'] );
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
if ( isset( $request['content'] ) ) {
|
|
217
|
+
$update_data['post_content'] = wp_kses_post( $request['content'] );
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
if ( isset( $request['status'] ) ) {
|
|
221
|
+
$update_data['post_status'] = sanitize_text_field( $request['status'] );
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
$result = wp_update_post( $update_data, true );
|
|
225
|
+
|
|
226
|
+
if ( is_wp_error( $result ) ) {
|
|
227
|
+
return new WP_Error(
|
|
228
|
+
'update_failed',
|
|
229
|
+
__( 'Failed to update item', 'my-plugin' ),
|
|
230
|
+
array( 'status' => 500 )
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
$updated_item = get_post( $id );
|
|
235
|
+
$data = array(
|
|
236
|
+
'id' => $updated_item->ID,
|
|
237
|
+
'title' => $updated_item->post_title,
|
|
238
|
+
'content' => $updated_item->post_content,
|
|
239
|
+
'status' => $updated_item->post_status,
|
|
240
|
+
);
|
|
241
|
+
|
|
242
|
+
return new WP_REST_Response( $data, 200 );
|
|
243
|
+
}
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
### DELETE - Delete Item
|
|
247
|
+
|
|
248
|
+
```php
|
|
249
|
+
<?php
|
|
250
|
+
/**
|
|
251
|
+
* Delete item
|
|
252
|
+
*/
|
|
253
|
+
function my_plugin_delete_item( $request ) {
|
|
254
|
+
$id = $request['id'];
|
|
255
|
+
|
|
256
|
+
// Check if item exists
|
|
257
|
+
$item = get_post( $id );
|
|
258
|
+
if ( ! $item || $item->post_type !== 'my_custom_post_type' ) {
|
|
259
|
+
return new WP_Error(
|
|
260
|
+
'not_found',
|
|
261
|
+
__( 'Item not found', 'my-plugin' ),
|
|
262
|
+
array( 'status' => 404 )
|
|
263
|
+
);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// Delete permanently (true) or move to trash (false)
|
|
267
|
+
$result = wp_delete_post( $id, true );
|
|
268
|
+
|
|
269
|
+
if ( ! $result ) {
|
|
270
|
+
return new WP_Error(
|
|
271
|
+
'delete_failed',
|
|
272
|
+
__( 'Failed to delete item', 'my-plugin' ),
|
|
273
|
+
array( 'status' => 500 )
|
|
274
|
+
);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
return new WP_REST_Response(
|
|
278
|
+
array(
|
|
279
|
+
'deleted' => true,
|
|
280
|
+
'id' => $id,
|
|
281
|
+
),
|
|
282
|
+
200
|
|
283
|
+
);
|
|
284
|
+
}
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
---
|
|
288
|
+
|
|
289
|
+
## Permission Callbacks
|
|
290
|
+
|
|
291
|
+
### Public Endpoint (No Authentication)
|
|
292
|
+
|
|
293
|
+
```php
|
|
294
|
+
<?php
|
|
295
|
+
'permission_callback' => '__return_true',
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
### Require Authentication
|
|
299
|
+
|
|
300
|
+
```php
|
|
301
|
+
<?php
|
|
302
|
+
function my_plugin_check_authentication() {
|
|
303
|
+
return is_user_logged_in();
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
'permission_callback' => 'my_plugin_check_authentication',
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
### Require Specific Capability
|
|
310
|
+
|
|
311
|
+
```php
|
|
312
|
+
<?php
|
|
313
|
+
function my_plugin_create_item_permissions_check( $request ) {
|
|
314
|
+
return current_user_can( 'edit_posts' );
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
function my_plugin_delete_item_permissions_check( $request ) {
|
|
318
|
+
return current_user_can( 'delete_posts' );
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
'permission_callback' => 'my_plugin_create_item_permissions_check',
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
### Custom Permission Logic
|
|
325
|
+
|
|
326
|
+
```php
|
|
327
|
+
<?php
|
|
328
|
+
function my_plugin_update_item_permissions_check( $request ) {
|
|
329
|
+
// Check if user is logged in
|
|
330
|
+
if ( ! is_user_logged_in() ) {
|
|
331
|
+
return new WP_Error(
|
|
332
|
+
'rest_forbidden',
|
|
333
|
+
__( 'You must be logged in to update items.', 'my-plugin' ),
|
|
334
|
+
array( 'status' => 401 )
|
|
335
|
+
);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// Check if user has capability
|
|
339
|
+
if ( ! current_user_can( 'edit_posts' ) ) {
|
|
340
|
+
return new WP_Error(
|
|
341
|
+
'rest_forbidden',
|
|
342
|
+
__( 'You do not have permission to edit items.', 'my-plugin' ),
|
|
343
|
+
array( 'status' => 403 )
|
|
344
|
+
);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// Check if user owns the resource
|
|
348
|
+
$id = $request['id'];
|
|
349
|
+
$post = get_post( $id );
|
|
350
|
+
|
|
351
|
+
if ( $post && $post->post_author != get_current_user_id() && ! current_user_can( 'edit_others_posts' ) ) {
|
|
352
|
+
return new WP_Error(
|
|
353
|
+
'rest_forbidden',
|
|
354
|
+
__( 'You can only edit your own items.', 'my-plugin' ),
|
|
355
|
+
array( 'status' => 403 )
|
|
356
|
+
);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
return true;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
'permission_callback' => 'my_plugin_update_item_permissions_check',
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
---
|
|
366
|
+
|
|
367
|
+
## Input Validation and Sanitization
|
|
368
|
+
|
|
369
|
+
### Define Argument Schema
|
|
370
|
+
|
|
371
|
+
```php
|
|
372
|
+
<?php
|
|
373
|
+
/**
|
|
374
|
+
* Get item schema for validation
|
|
375
|
+
*/
|
|
376
|
+
function my_plugin_get_item_schema() {
|
|
377
|
+
return array(
|
|
378
|
+
'title' => array(
|
|
379
|
+
'required' => true,
|
|
380
|
+
'type' => 'string',
|
|
381
|
+
'description' => __( 'Item title', 'my-plugin' ),
|
|
382
|
+
'sanitize_callback' => 'sanitize_text_field',
|
|
383
|
+
),
|
|
384
|
+
'content' => array(
|
|
385
|
+
'required' => false,
|
|
386
|
+
'type' => 'string',
|
|
387
|
+
'description' => __( 'Item content', 'my-plugin' ),
|
|
388
|
+
'sanitize_callback' => 'wp_kses_post',
|
|
389
|
+
),
|
|
390
|
+
'status' => array(
|
|
391
|
+
'required' => false,
|
|
392
|
+
'type' => 'string',
|
|
393
|
+
'description' => __( 'Item status', 'my-plugin' ),
|
|
394
|
+
'enum' => array( 'draft', 'publish', 'private' ),
|
|
395
|
+
'default' => 'draft',
|
|
396
|
+
'sanitize_callback' => 'sanitize_text_field',
|
|
397
|
+
),
|
|
398
|
+
'count' => array(
|
|
399
|
+
'required' => false,
|
|
400
|
+
'type' => 'integer',
|
|
401
|
+
'description' => __( 'Item count', 'my-plugin' ),
|
|
402
|
+
'minimum' => 0,
|
|
403
|
+
'maximum' => 100,
|
|
404
|
+
'default' => 0,
|
|
405
|
+
),
|
|
406
|
+
);
|
|
407
|
+
}
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
### Custom Validation
|
|
411
|
+
|
|
412
|
+
```php
|
|
413
|
+
<?php
|
|
414
|
+
/**
|
|
415
|
+
* Validate email
|
|
416
|
+
*/
|
|
417
|
+
function my_plugin_validate_email( $param, $request, $key ) {
|
|
418
|
+
if ( ! is_email( $param ) ) {
|
|
419
|
+
return new WP_Error(
|
|
420
|
+
'invalid_email',
|
|
421
|
+
__( 'Invalid email address', 'my-plugin' )
|
|
422
|
+
);
|
|
423
|
+
}
|
|
424
|
+
return true;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
/**
|
|
428
|
+
* Validate URL
|
|
429
|
+
*/
|
|
430
|
+
function my_plugin_validate_url( $param, $request, $key ) {
|
|
431
|
+
if ( ! filter_var( $param, FILTER_VALIDATE_URL ) ) {
|
|
432
|
+
return new WP_Error(
|
|
433
|
+
'invalid_url',
|
|
434
|
+
__( 'Invalid URL', 'my-plugin' )
|
|
435
|
+
);
|
|
436
|
+
}
|
|
437
|
+
return true;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
// Use in args
|
|
441
|
+
'args' => array(
|
|
442
|
+
'email' => array(
|
|
443
|
+
'required' => true,
|
|
444
|
+
'validate_callback' => 'my_plugin_validate_email',
|
|
445
|
+
'sanitize_callback' => 'sanitize_email',
|
|
446
|
+
),
|
|
447
|
+
'website' => array(
|
|
448
|
+
'required' => false,
|
|
449
|
+
'validate_callback' => 'my_plugin_validate_url',
|
|
450
|
+
'sanitize_callback' => 'esc_url_raw',
|
|
451
|
+
),
|
|
452
|
+
),
|
|
453
|
+
|
|
454
|
+
---
|
|
455
|
+
|
|
456
|
+
## Query Parameters
|
|
457
|
+
|
|
458
|
+
### Accept Query Parameters
|
|
459
|
+
|
|
460
|
+
```php
|
|
461
|
+
<?php
|
|
462
|
+
/**
|
|
463
|
+
* Get collection parameters
|
|
464
|
+
*/
|
|
465
|
+
function my_plugin_get_collection_params() {
|
|
466
|
+
return array(
|
|
467
|
+
'per_page' => array(
|
|
468
|
+
'description' => __( 'Items per page', 'my-plugin' ),
|
|
469
|
+
'type' => 'integer',
|
|
470
|
+
'default' => 10,
|
|
471
|
+
'minimum' => 1,
|
|
472
|
+
'maximum' => 100,
|
|
473
|
+
'sanitize_callback' => 'absint',
|
|
474
|
+
),
|
|
475
|
+
'page' => array(
|
|
476
|
+
'description' => __( 'Page number', 'my-plugin' ),
|
|
477
|
+
'type' => 'integer',
|
|
478
|
+
'default' => 1,
|
|
479
|
+
'minimum' => 1,
|
|
480
|
+
'sanitize_callback' => 'absint',
|
|
481
|
+
),
|
|
482
|
+
'search' => array(
|
|
483
|
+
'description' => __( 'Search term', 'my-plugin' ),
|
|
484
|
+
'type' => 'string',
|
|
485
|
+
'sanitize_callback' => 'sanitize_text_field',
|
|
486
|
+
),
|
|
487
|
+
'orderby' => array(
|
|
488
|
+
'description' => __( 'Order by field', 'my-plugin' ),
|
|
489
|
+
'type' => 'string',
|
|
490
|
+
'default' => 'date',
|
|
491
|
+
'enum' => array( 'date', 'title', 'modified' ),
|
|
492
|
+
'sanitize_callback' => 'sanitize_text_field',
|
|
493
|
+
),
|
|
494
|
+
'order' => array(
|
|
495
|
+
'description' => __( 'Order direction', 'my-plugin' ),
|
|
496
|
+
'type' => 'string',
|
|
497
|
+
'default' => 'DESC',
|
|
498
|
+
'enum' => array( 'ASC', 'DESC' ),
|
|
499
|
+
'sanitize_callback' => 'sanitize_text_field',
|
|
500
|
+
),
|
|
501
|
+
);
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
/**
|
|
505
|
+
* Get items with query parameters
|
|
506
|
+
*/
|
|
507
|
+
function my_plugin_get_items( $request ) {
|
|
508
|
+
$per_page = $request->get_param( 'per_page' );
|
|
509
|
+
$page = $request->get_param( 'page' );
|
|
510
|
+
$search = $request->get_param( 'search' );
|
|
511
|
+
$orderby = $request->get_param( 'orderby' );
|
|
512
|
+
$order = $request->get_param( 'order' );
|
|
513
|
+
|
|
514
|
+
$args = array(
|
|
515
|
+
'post_type' => 'my_custom_post_type',
|
|
516
|
+
'posts_per_page' => $per_page,
|
|
517
|
+
'paged' => $page,
|
|
518
|
+
'orderby' => $orderby,
|
|
519
|
+
'order' => $order,
|
|
520
|
+
);
|
|
521
|
+
|
|
522
|
+
if ( ! empty( $search ) ) {
|
|
523
|
+
$args['s'] = $search;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
$query = new WP_Query( $args );
|
|
527
|
+
|
|
528
|
+
$items = array();
|
|
529
|
+
foreach ( $query->posts as $post ) {
|
|
530
|
+
$items[] = array(
|
|
531
|
+
'id' => $post->ID,
|
|
532
|
+
'title' => $post->post_title,
|
|
533
|
+
'content' => $post->post_content,
|
|
534
|
+
'date' => $post->post_date,
|
|
535
|
+
);
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
$response = new WP_REST_Response( $items, 200 );
|
|
539
|
+
|
|
540
|
+
// Add pagination headers
|
|
541
|
+
$response->header( 'X-WP-Total', $query->found_posts );
|
|
542
|
+
$response->header( 'X-WP-TotalPages', $query->max_num_pages );
|
|
543
|
+
|
|
544
|
+
return $response;
|
|
545
|
+
}
|
|
546
|
+
```
|
|
547
|
+
|
|
548
|
+
**Usage:**
|
|
549
|
+
```
|
|
550
|
+
GET /wp-json/my-plugin/v1/items?per_page=20&page=2&orderby=title&order=ASC&search=keyword
|
|
551
|
+
```
|
|
552
|
+
|
|
553
|
+
---
|
|
554
|
+
|
|
555
|
+
## Authentication
|
|
556
|
+
|
|
557
|
+
### Application Passwords (WordPress 5.6+)
|
|
558
|
+
|
|
559
|
+
WordPress natively supports Application Passwords for REST API authentication.
|
|
560
|
+
|
|
561
|
+
**No additional code needed** - Users create application passwords in their profile.
|
|
562
|
+
|
|
563
|
+
**Usage:**
|
|
564
|
+
```bash
|
|
565
|
+
curl -X GET https://example.com/wp-json/my-plugin/v1/items \
|
|
566
|
+
--user "username:application-password"
|
|
567
|
+
```
|
|
568
|
+
|
|
569
|
+
### Custom API Key Authentication
|
|
570
|
+
|
|
571
|
+
```php
|
|
572
|
+
<?php
|
|
573
|
+
/**
|
|
574
|
+
* Validate API key
|
|
575
|
+
*/
|
|
576
|
+
function my_plugin_validate_api_key( $request ) {
|
|
577
|
+
$api_key = $request->get_header( 'X-API-Key' );
|
|
578
|
+
|
|
579
|
+
if ( empty( $api_key ) ) {
|
|
580
|
+
return new WP_Error(
|
|
581
|
+
'rest_forbidden',
|
|
582
|
+
__( 'API key is required', 'my-plugin' ),
|
|
583
|
+
array( 'status' => 401 )
|
|
584
|
+
);
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
// Validate API key against stored keys
|
|
588
|
+
$valid_keys = get_option( 'my_plugin_api_keys', array() );
|
|
589
|
+
|
|
590
|
+
if ( ! in_array( $api_key, $valid_keys, true ) ) {
|
|
591
|
+
return new WP_Error(
|
|
592
|
+
'rest_forbidden',
|
|
593
|
+
__( 'Invalid API key', 'my-plugin' ),
|
|
594
|
+
array( 'status' => 401 )
|
|
595
|
+
);
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
return true;
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
// Use in endpoint
|
|
602
|
+
register_rest_route( 'my-plugin/v1', '/secure-data', array(
|
|
603
|
+
'methods' => 'GET',
|
|
604
|
+
'callback' => 'my_plugin_get_secure_data',
|
|
605
|
+
'permission_callback' => 'my_plugin_validate_api_key',
|
|
606
|
+
) );
|
|
607
|
+
```
|
|
608
|
+
|
|
609
|
+
**Usage:**
|
|
610
|
+
```bash
|
|
611
|
+
curl -X GET https://example.com/wp-json/my-plugin/v1/secure-data \
|
|
612
|
+
-H "X-API-Key: your-api-key-here"
|
|
613
|
+
```
|
|
614
|
+
|
|
615
|
+
---
|
|
616
|
+
|
|
617
|
+
## Modify Existing Endpoints
|
|
618
|
+
|
|
619
|
+
### Add Custom Fields to Posts
|
|
620
|
+
|
|
621
|
+
```php
|
|
622
|
+
<?php
|
|
623
|
+
/**
|
|
624
|
+
* Add custom field to post response
|
|
625
|
+
*/
|
|
626
|
+
function my_plugin_add_custom_field_to_post() {
|
|
627
|
+
register_rest_field( 'post', 'custom_field', array(
|
|
628
|
+
'get_callback' => 'my_plugin_get_custom_field',
|
|
629
|
+
'update_callback' => 'my_plugin_update_custom_field',
|
|
630
|
+
'schema' => array(
|
|
631
|
+
'description' => __( 'Custom field', 'my-plugin' ),
|
|
632
|
+
'type' => 'string',
|
|
633
|
+
),
|
|
634
|
+
) );
|
|
635
|
+
}
|
|
636
|
+
add_action( 'rest_api_init', 'my_plugin_add_custom_field_to_post' );
|
|
637
|
+
|
|
638
|
+
/**
|
|
639
|
+
* Get custom field value
|
|
640
|
+
*/
|
|
641
|
+
function my_plugin_get_custom_field( $post ) {
|
|
642
|
+
return get_post_meta( $post['id'], '_custom_field', true );
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
/**
|
|
646
|
+
* Update custom field value
|
|
647
|
+
*/
|
|
648
|
+
function my_plugin_update_custom_field( $value, $post ) {
|
|
649
|
+
return update_post_meta( $post->ID, '_custom_field', sanitize_text_field( $value ) );
|
|
650
|
+
}
|
|
651
|
+
```
|
|
652
|
+
|
|
653
|
+
### Add Multiple Custom Fields
|
|
654
|
+
|
|
655
|
+
```php
|
|
656
|
+
<?php
|
|
657
|
+
/**
|
|
658
|
+
* Add multiple custom fields
|
|
659
|
+
*/
|
|
660
|
+
function my_plugin_add_multiple_fields() {
|
|
661
|
+
register_rest_field( 'post', 'view_count', array(
|
|
662
|
+
'get_callback' => function( $post ) {
|
|
663
|
+
return (int) get_post_meta( $post['id'], 'views', true );
|
|
664
|
+
},
|
|
665
|
+
'schema' => array(
|
|
666
|
+
'description' => __( 'Number of views', 'my-plugin' ),
|
|
667
|
+
'type' => 'integer',
|
|
668
|
+
),
|
|
669
|
+
) );
|
|
670
|
+
|
|
671
|
+
register_rest_field( 'post', 'reading_time', array(
|
|
672
|
+
'get_callback' => function( $post ) {
|
|
673
|
+
$content = get_post_field( 'post_content', $post['id'] );
|
|
674
|
+
$word_count = str_word_count( strip_tags( $content ) );
|
|
675
|
+
return ceil( $word_count / 200 ); // Assume 200 words per minute
|
|
676
|
+
},
|
|
677
|
+
'schema' => array(
|
|
678
|
+
'description' => __( 'Estimated reading time in minutes', 'my-plugin' ),
|
|
679
|
+
'type' => 'integer',
|
|
680
|
+
),
|
|
681
|
+
) );
|
|
682
|
+
}
|
|
683
|
+
add_action( 'rest_api_init', 'my_plugin_add_multiple_fields' );
|
|
684
|
+
```
|
|
685
|
+
|
|
686
|
+
### Modify Response Data
|
|
687
|
+
|
|
688
|
+
```php
|
|
689
|
+
<?php
|
|
690
|
+
/**
|
|
691
|
+
* Modify post response
|
|
692
|
+
*/
|
|
693
|
+
function my_plugin_modify_post_response( $response, $post, $request ) {
|
|
694
|
+
$data = $response->get_data();
|
|
695
|
+
|
|
696
|
+
// Add custom data
|
|
697
|
+
$data['custom_data'] = array(
|
|
698
|
+
'views' => get_post_meta( $post->ID, 'views', true ),
|
|
699
|
+
'likes' => get_post_meta( $post->ID, 'likes', true ),
|
|
700
|
+
'is_featured' => (bool) get_post_meta( $post->ID, 'featured', true ),
|
|
701
|
+
);
|
|
702
|
+
|
|
703
|
+
// Remove sensitive data
|
|
704
|
+
unset( $data['author'] );
|
|
705
|
+
|
|
706
|
+
$response->set_data( $data );
|
|
707
|
+
|
|
708
|
+
return $response;
|
|
709
|
+
}
|
|
710
|
+
add_filter( 'rest_prepare_post', 'my_plugin_modify_post_response', 10, 3 );
|
|
711
|
+
```
|
|
712
|
+
|
|
713
|
+
---
|
|
714
|
+
|
|
715
|
+
## Best Practices
|
|
716
|
+
|
|
717
|
+
### Endpoint Design
|
|
718
|
+
|
|
719
|
+
1. **Use versioning**: Include version in namespace (`my-plugin/v1`)
|
|
720
|
+
2. **Use nouns for resources**: `/items`, `/users`, not `/get-items`
|
|
721
|
+
3. **Use HTTP methods correctly**: GET (read), POST (create), PUT/PATCH (update), DELETE (delete)
|
|
722
|
+
4. **Use proper status codes**: 200 (OK), 201 (Created), 400 (Bad Request), 401 (Unauthorized), 403 (Forbidden), 404 (Not Found), 500 (Server Error)
|
|
723
|
+
5. **Return consistent responses**: Use `WP_REST_Response` or `WP_Error`
|
|
724
|
+
|
|
725
|
+
### Security
|
|
726
|
+
|
|
727
|
+
1. **Always validate permissions**: Never use `__return_true` for write operations
|
|
728
|
+
2. **Sanitize input**: Use appropriate sanitization callbacks
|
|
729
|
+
3. **Validate input**: Use validation callbacks and schema
|
|
730
|
+
4. **Escape output**: Use `esc_html()`, `esc_url()`, etc.
|
|
731
|
+
5. **Use nonces for admin**: For admin-ajax.php endpoints (not REST API)
|
|
732
|
+
6. **Rate limiting**: Consider implementing rate limiting for public endpoints
|
|
733
|
+
|
|
734
|
+
### Performance
|
|
735
|
+
|
|
736
|
+
1. **Limit query results**: Set maximum `per_page` value
|
|
737
|
+
2. **Use pagination**: Always paginate large result sets
|
|
738
|
+
3. **Cache responses**: Use transients for expensive queries
|
|
739
|
+
4. **Optimize queries**: Use `WP_Query` efficiently
|
|
740
|
+
5. **Lazy load data**: Don't include unnecessary data in responses
|
|
741
|
+
|
|
742
|
+
### Documentation
|
|
743
|
+
|
|
744
|
+
1. **Document endpoints**: Provide clear descriptions
|
|
745
|
+
2. **Document parameters**: Describe all parameters and their types
|
|
746
|
+
3. **Provide examples**: Include example requests and responses
|
|
747
|
+
4. **Use schema**: Define proper schema for validation and documentation
|
|
748
|
+
|
|
749
|
+
---
|
|
750
|
+
|
|
751
|
+
## Common Pitfalls
|
|
752
|
+
|
|
753
|
+
### ❌ DON'T
|
|
754
|
+
|
|
755
|
+
```php
|
|
756
|
+
// Don't use __return_true for write operations
|
|
757
|
+
register_rest_route( 'my-plugin/v1', '/items', array(
|
|
758
|
+
'methods' => 'POST',
|
|
759
|
+
'callback' => 'create_item',
|
|
760
|
+
'permission_callback' => '__return_true', // BAD!
|
|
761
|
+
) );
|
|
762
|
+
|
|
763
|
+
// Don't forget to sanitize input
|
|
764
|
+
function create_item( $request ) {
|
|
765
|
+
$title = $request['title']; // BAD! Not sanitized
|
|
766
|
+
wp_insert_post( array( 'post_title' => $title ) );
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
// Don't return raw data
|
|
770
|
+
function get_item( $request ) {
|
|
771
|
+
return get_post( $request['id'] ); // BAD! Returns WP_Post object
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
// Don't forget error handling
|
|
775
|
+
function get_item( $request ) {
|
|
776
|
+
$item = get_post( $request['id'] );
|
|
777
|
+
return new WP_REST_Response( $item, 200 ); // BAD! No error check
|
|
778
|
+
}
|
|
779
|
+
```
|
|
780
|
+
|
|
781
|
+
### ✅ DO
|
|
782
|
+
|
|
783
|
+
```php
|
|
784
|
+
// Use proper permission checks
|
|
785
|
+
register_rest_route( 'my-plugin/v1', '/items', array(
|
|
786
|
+
'methods' => 'POST',
|
|
787
|
+
'callback' => 'create_item',
|
|
788
|
+
'permission_callback' => 'check_create_permission', // GOOD!
|
|
789
|
+
) );
|
|
790
|
+
|
|
791
|
+
// Always sanitize input
|
|
792
|
+
function create_item( $request ) {
|
|
793
|
+
$title = sanitize_text_field( $request['title'] ); // GOOD!
|
|
794
|
+
wp_insert_post( array( 'post_title' => $title ) );
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
// Return formatted data
|
|
798
|
+
function get_item( $request ) {
|
|
799
|
+
$post = get_post( $request['id'] );
|
|
800
|
+
$data = array(
|
|
801
|
+
'id' => $post->ID,
|
|
802
|
+
'title' => $post->post_title,
|
|
803
|
+
);
|
|
804
|
+
return new WP_REST_Response( $data, 200 ); // GOOD!
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
// Always handle errors
|
|
808
|
+
function get_item( $request ) {
|
|
809
|
+
$item = get_post( $request['id'] );
|
|
810
|
+
|
|
811
|
+
if ( ! $item ) {
|
|
812
|
+
return new WP_Error( 'not_found', 'Item not found', array( 'status' => 404 ) );
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
return new WP_REST_Response( $item, 200 ); // GOOD!
|
|
816
|
+
}
|
|
817
|
+
```
|
|
818
|
+
|