Write Operations: Create, Update & Delete
Creating Content
Use POST requests to create new content.
Creating a Post
async function createPost(data) {
const response = await fetch('/wp-json/wp/v2/posts', {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
title: data.title,
content: data.content,
excerpt: data.excerpt,
status: 'publish', // or 'draft'
categories: [5, 6],
tags: [10, 11],
featured_media: 123, // Media ID
meta: {
custom_field: 'value'
}
})
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.message);
}
return response.json(); // Returns created post with ID
}Creating a Page
async function createPage(title, content, parentId = 0) {
const response = await fetch('/wp-json/wp/v2/pages', {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
title,
content,
status: 'publish',
parent: parentId, // For child pages
menu_order: 0,
template: '' // Page template slug
})
});
return response.json();
}Uploading Media
async function uploadMedia(file) {
const formData = new FormData();
formData.append('file', file);
formData.append('title', file.name);
formData.append('alt_text', 'Image description');
const response = await fetch('/wp-json/wp/v2/media', {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`
// Don't set Content-Type - browser sets it with boundary
},
body: formData
});
return response.json(); // Returns media object with ID
}
// Usage with featured image
const media = await uploadMedia(imageFile);
const post = await createPost({
title: 'My Post',
content: 'Post content',
featured_media: media.id
});Updating Content
Use PUT (full update) or PATCH (partial update).
Updating a Post
async function updatePost(postId, updates) {
const response = await fetch(`/wp-json/wp/v2/posts/${postId}`, {
method: 'PUT', // or 'PATCH' for partial
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(updates)
});
return response.json();
}
// Update only specific fields
await updatePost(123, {
title: 'Updated Title'
});
// Update multiple fields
await updatePost(123, {
title: 'New Title',
content: 'Updated content',
status: 'draft',
categories: [7, 8]
});Updating Meta Fields
// Meta fields must be registered with 'show_in_rest' => true
await updatePost(123, {
meta: {
_custom_field: 'new value',
_another_field: 42
}
});
Important: Custom meta fields must be registered with
register_post_meta() and show_in_rest => true to be accessible via the API.
// In your plugin or theme
register_post_meta('post', '_custom_field', [
'show_in_rest' => true,
'single' => true,
'type' => 'string',
'auth_callback' => function() {
return current_user_can('edit_posts');
}
]);Deleting Content
Use DELETE requests. By default, posts go to trash.
Delete (Trash) a Post
async function trashPost(postId) {
const response = await fetch(`/wp-json/wp/v2/posts/${postId}`, {
method: 'DELETE',
headers: {
'Authorization': `Bearer ${token}`
}
});
return response.json(); // Returns trashed post
}Permanently Delete
async function deletePost(postId, force = false) {
const url = force
? `/wp-json/wp/v2/posts/${postId}?force=true`
: `/wp-json/wp/v2/posts/${postId}`;
const response = await fetch(url, {
method: 'DELETE',
headers: {
'Authorization': `Bearer ${token}`
}
});
return response.json();
}
// Trash (can be restored)
await deletePost(123);
// Permanently delete
await deletePost(123, true);Working with Terms
Create a Category
async function createCategory(name, parent = 0) {
const response = await fetch('/wp-json/wp/v2/categories', {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
name,
parent,
description: '',
slug: '' // Auto-generated if empty
})
});
return response.json();
}Assign Terms to Post
// Categories and tags are arrays of term IDs
await updatePost(123, {
categories: [1, 2, 3],
tags: [10, 11, 12]
});Bulk Operations
The API doesn't have built-in bulk operations, but you can use Promise.all:
async function bulkUpdateStatus(postIds, status) {
const updates = postIds.map(id =>
updatePost(id, { status })
);
return Promise.all(updates);
}
// Update multiple posts to draft
await bulkUpdateStatus([1, 2, 3, 4, 5], 'draft');
Performance Tip: For many updates, consider creating a custom batch endpoint in your plugin to reduce HTTP overhead.
Error Handling
async function safeApiCall(endpoint, options) {
try {
const response = await fetch(endpoint, options);
const data = await response.json();
if (!response.ok) {
// WordPress returns error details
return {
success: false,
code: data.code,
message: data.message,
data: data.data
};
}
return { success: true, data };
} catch (error) {
return {
success: false,
code: 'network_error',
message: error.message
};
}
}
// Usage
const result = await safeApiCall('/wp-json/wp/v2/posts', {
method: 'POST',
headers: { /* ... */ },
body: JSON.stringify({ title: '' }) // Empty title = error
});
if (!result.success) {
console.error(`${result.code}: ${result.message}`);
// "rest_invalid_param: title is required"
}Next Steps
In the next lesson, we'll build custom REST endpoints for your own data and functionality.
๐ฏ Lesson Complete! You can now create, update, and delete WordPress content through the REST API.