Learn to handle file uploads in the WordPress REST API. We'll cover multipart/form-data, media attachment, and securing file processing in your plugin.
Previously in this course, we explored building search and filter functionality in the WordPress REST API to improve data retrieval. Today, we advance our Knowledge Base plugin by enabling users to upload and attach media directly through our admin interface.
Handling file uploads via the REST API is different from standard JSON-based requests because files require multipart/form-data encoding. We need to bridge the gap between binary file streams and WordPress's media management system.
In WordPress, media is just another post type (attachment). When you upload a file, WordPress creates a post entry, generates metadata, and stores the file in the uploads directory.
To do this via the REST API, we must:
FormData to package the file.media_handle_upload (or wp_handle_upload), and create an attachment post.We need a dedicated endpoint for uploads. Using the POST method is standard here. We will use media_handle_upload because it handles the heavy lifting: moving the file, generating thumbnails, and creating the attachment post record.
PHP#6A9955">// In your REST API class public function register_routes() { register_rest_route('kb/v1', '/upload', [ 'methods' => 'POST', 'callback' => [$this, 'handle_upload'], 'permission_callback' => [$this, 'permissions_check'], ]); } public function handle_upload($request) { if (empty($_FILES['file'])) { return new WP_Error('no_file', 'No file uploaded', ['status' => 400]); } require_once ABSPATH . 'wp-admin/includes/image.php'; require_once ABSPATH . 'wp-admin/includes/file.php'; require_once ABSPATH . 'wp-admin/includes/media.php'; $attachment_id = media_handle_upload('file', 0); if (is_wp_error($attachment_id)) { return $attachment_id; } return ['id' => $attachment_id, 'url' => wp_get_attachment_url($attachment_id)]; }
Once you have the $attachment_id, you can link it to your Knowledge Base entry. Since we already covered creating POST endpoints for data submission, you should update your existing update endpoint to accept an attachment_id and store it as post meta:
PHPupdate_post_meta($post_id, '_kb_featured_image', absint($request['attachment_id']));
In your React admin dashboard, you cannot use the standard apiFetch JSON body. You must construct a FormData object.
const file = event.target.files[0];.JAVASCRIPTconst formData = new FormData(); formData.append(CE9178">'file', file); const response = await apiFetch({ path: CE9178">'/kb/v1/upload', method: CE9178">'POST', body: formData, // apiFetch handles multipart headers automatically });
media_handle_upload is not loaded by default on the frontend. You must include media.php as shown above, or WordPress will throw a fatal error.permission_callback checks for upload_files capability. Never allow anonymous uploads.upload_max_filesize or post_max_size limits in php.ini. If the API returns a 500 error on large files, check your server configuration.POST request, ensure you are passing the correct nonce, as discussed in our implementing nonce verification lesson.We have enabled file uploads by creating a dedicated REST endpoint using media_handle_upload. By pairing this with FormData in React, we allow seamless attachment management within our plugin. This completes the media pipeline for our Knowledge Base.
Up next: Optimizing API Response Times, where we will implement object caching to keep our endpoints snappy.
Learn to enforce strict schemas and validate nested JSON objects in your WordPress REST API to ensure data integrity and security in your plugin.
Read moreLearn to secure your WordPress REST API against CSRF attacks. Master generating nonces, passing them via headers, and verifying them in your API endpoints.
Managing File Uploads via REST API
Integrating with Gutenberg Blocks
Handling Conflict Resolution
Building a Modal Confirmation System
Implementing Activity Logging
Using Webpack Aliases
Unit Testing API Endpoints
Unit Testing React Components
Handling Large Datasets with GraphQL
Implementing Real-time Updates with Web