wordpress-middleware
v1.1.0
Published
Typed WordPress REST API wrapper for Node.js (Express middleware) and browser (React client). Manage posts, media, and contact forms with clean, structured responses.
Maintainers
Readme
wordpress-middleware
Typed WordPress REST API wrapper for Node.js (Express middleware) and browser (React client). Manage posts, media, and contact forms with clean, structured responses.
Installation
npm install wordpress-middlewareTwo Ways to Use
| Import | Environment | Use Case |
|--------|-------------|----------|
| wordpress-middleware | Node.js / Express | Mount ready-made routes in your backend |
| wordpress-middleware/client | Browser / React | Call WordPress directly from your frontend |
Client-Side (React / Browser)
Zero Node.js dependencies — uses native fetch.
import { WordPressClient } from 'wordpress-middleware/client';
const wp = new WordPressClient({
baseUrl: 'https://your-wordpress-site.com',
});Fetching Posts
// List posts — returns an array of PostResponse objects
const posts = await wp.posts.list({ per_page: 5 });
// posts = [{ id, title, slug, content, excerpt, status, ... }, ...]
// Get a single post
const post = await wp.posts.getById(123);
// Get by slug (useful for routing)
const post = await wp.posts.getBySlug('hello-world');
// Search
const results = await wp.posts.search('keyword');Creating & Updating Posts
// Requires auth config
const wp = new WordPressClient({
baseUrl: 'https://your-site.com',
username: 'your-username',
password: 'your-application-password',
});
const post = await wp.posts.create({
title: 'My Post',
content: '<p>Hello world</p>',
status: 'publish',
});
await wp.posts.update(post.id, { title: 'Updated Title' });
await wp.posts.delete(post.id);Uploading Media
const fileInput = document.querySelector<HTMLInputElement>('input[type="file"]');
const file = fileInput.files[0];
const media = await wp.media.upload(file, {
title: 'My Image',
alt_text: 'A description',
});
// Attach as featured image
await wp.posts.update(postId, { featured_media: media.id });Contact Forms (Contact Form 7)
Requires the Contact Form 7 plugin installed on your WordPress site.
// Submit a contact form (no auth required for submission)
const result = await wp.contact.submit(123, {
'your-name': 'John Doe',
'your-email': '[email protected]',
'your-subject': 'Hello',
'your-message': 'I would like to get in touch.',
});
console.log(result.success); // true
console.log(result.message); // "Thank you for your message. It has been sent."
// Handle validation errors
if (!result.success && result.invalidFields) {
result.invalidFields.forEach(({ field, message }) => {
console.log(`${field}: ${message}`);
});
}
// List available forms (requires auth)
const forms = await wp.contact.listForms();
// Get form details
const form = await wp.contact.getForm(123);React Contact Form Example
import { useState } from 'react';
import { WordPressClient } from 'wordpress-middleware/client';
import type { ContactSubmitResponse } from 'wordpress-middleware/client';
const wp = new WordPressClient({
baseUrl: 'https://your-wordpress-site.com',
});
const FORM_ID = 123; // Your CF7 form ID
function ContactForm() {
const [status, setStatus] = useState<ContactSubmitResponse | null>(null);
const [loading, setLoading] = useState(false);
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
setLoading(true);
const formData = new FormData(e.currentTarget);
const result = await wp.contact.submit(FORM_ID, {
'your-name': formData.get('name') as string,
'your-email': formData.get('email') as string,
'your-subject': formData.get('subject') as string,
'your-message': formData.get('message') as string,
});
setStatus(result);
setLoading(false);
if (result.success) {
e.currentTarget.reset();
}
};
return (
<form onSubmit={handleSubmit}>
<input name="name" placeholder="Full Name" required />
<input name="email" type="email" placeholder="Email" required />
<input name="subject" placeholder="Subject" required />
<textarea name="message" placeholder="Message" required />
<button type="submit" disabled={loading}>
{loading ? 'Sending...' : 'Send Message'}
</button>
{status && (
<p style={{ color: status.success ? 'green' : 'red' }}>
{status.message}
</p>
)}
</form>
);
}Error Handling
import { WordPressClient, WPClientError } from 'wordpress-middleware/client';
try {
await wp.posts.getById(999);
} catch (error) {
if (error instanceof WPClientError) {
console.log(error.status); // 404
console.log(error.message); // "Invalid post ID."
console.log(error.code); // "rest_post_invalid_id"
}
}Server-Side (Express / Node.js)
Mount pre-built Express routes that proxy and transform WordPress API calls.
import express from 'express';
import { createPostsRouter, createMediaRouter, createContactRouter, errorHandler } from 'wordpress-middleware';
const app = express();
app.use(express.json());
const wpConfig = {
baseUrl: 'https://your-wordpress-site.com',
username: 'your-username',
password: 'your-application-password',
};
app.use('/api/posts', createPostsRouter(wpConfig));
app.use('/api/media', createMediaRouter(wpConfig));
app.use('/api/contact', createContactRouter(wpConfig));
app.use(errorHandler);
app.listen(3000);Endpoints
Posts
| Method | Endpoint | Description |
|--------|----------|-------------|
| GET | /api/posts | List posts (pagination, filtering) |
| GET | /api/posts/:id | Get post by ID |
| GET | /api/posts/slug/:slug | Get post by slug |
| GET | /api/posts/search?q=term | Search posts |
| POST | /api/posts | Create a post |
| PUT | /api/posts/:id | Update a post |
| DELETE | /api/posts/:id | Delete a post |
Media
| Method | Endpoint | Description |
|--------|----------|-------------|
| GET | /api/media | List media |
| GET | /api/media/:id | Get media by ID |
| POST | /api/media | Upload (multipart form, field: file) |
| DELETE | /api/media/:id | Delete media |
Contact (Contact Form 7)
| Method | Endpoint | Description |
|--------|----------|-------------|
| POST | /api/contact/submit/:formId | Submit a contact form |
| GET | /api/contact/forms | List available forms |
| GET | /api/contact/forms/:formId | Get form details |
Query Parameters
| Parameter | Type | Description |
|-----------|------|-------------|
| page | number | Page number (default: 1) |
| per_page | number | Items per page (default: 10) |
| search | string | Search term |
| categories | number[] | Filter by category IDs |
| tags | number[] | Filter by tag IDs |
| author | number | Filter by author ID |
| status | string | Filter by post status |
| orderby | string | date, title, id, slug |
| order | string | asc or desc |
Response Format
Both server and client return the same clean, structured JSON.
Post
{
"id": 123,
"title": "Hello World",
"slug": "hello-world",
"content": "<p>Post content here</p>",
"excerpt": "<p>Short summary</p>",
"status": "publish",
"date": "2026-02-18T10:30:00",
"modified": "2026-02-18T12:00:00",
"link": "https://your-site.com/hello-world",
"author": 1,
"featuredMedia": 42,
"categories": [1, 3],
"tags": [5]
}Media
{
"id": 42,
"title": "My Image",
"altText": "Description of image",
"caption": "Image caption",
"mimeType": "image/jpeg",
"sourceUrl": "https://your-site.com/wp-content/uploads/image.jpg",
"width": 1920,
"height": 1080,
"sizes": {
"thumbnail": { "url": "https://...", "width": 150, "height": 150 },
"medium": { "url": "https://...", "width": 300, "height": 200 }
}
}Contact Submit
{
"success": true,
"message": "Thank you for your message. It has been sent.",
"formId": 123
}On validation failure:
{
"success": false,
"message": "One or more fields have an error.",
"formId": 123,
"invalidFields": [
{ "field": "your-email", "message": "Please fill out this field." }
]
}Contact Form Info
{
"id": 123,
"title": "Contact form 1",
"slug": "contact-form-1",
"locale": "en_US"
}Paginated List
{
"data": [],
"total": 50,
"totalPages": 5,
"page": 1,
"perPage": 10
}Authentication
Both server and client support the same two methods:
Basic Auth — use a WordPress Application Password:
{
baseUrl: 'https://your-site.com',
username: 'your-username',
password: 'xxxx xxxx xxxx xxxx xxxx xxxx',
}Bearer Token (JWT):
{
baseUrl: 'https://your-site.com',
authToken: 'your-jwt-token',
}Contact Form 7 Setup
- Install the Contact Form 7 plugin on your WordPress site
- Go to Contact > Contact Forms in WordPress admin
- Create or edit a form — note the form ID from the URL (e.g.,
post=95means ID is95) - The field names in your form template (e.g.,
[text* your-name]) are the keys you pass tosubmit() - Submissions are sent as email to the address configured in the form's Mail tab
Default CF7 Field Names
| Field | CF7 Tag | Key to Use |
|-------|---------|------------|
| Name | [text* your-name] | your-name |
| Email | [email* your-email] | your-email |
| Subject | [text* your-subject] | your-subject |
| Message | [textarea* your-message] | your-message |
Post Statuses
| Status | Description |
|--------|-------------|
| draft | Not visible (default) |
| publish | Live and visible |
| pending | Awaiting review |
| private | Visible to authorized users only |
| future | Scheduled |
License
ISC
