How to Create and Update WordPress Pages Using the WP REST API in TypeScript

When building a custom integration with WordPress, interacting with the WordPress REST API is a powerful approach. Recently, I needed to create and update WordPress pages programmatically using TypeScript. While the task seemed straightforward, I encountered some challenges, like permission errors and authentication issues. After troubleshooting and refining the process, I found a robust way to manage WordPress pages via the /wp/v2/pages API. This post walks through the steps I took to resolve these challenges and create a TypeScript script that creates or updates pages seamlessly.

Thank me by sharing on Twitter 🙏

Creating or Updating Pages via API

The goal is simple: use the WordPress REST API to create a list of pages or update them if they already exist. The /wp/v2/pages API allows us to create, read, update, and delete WordPress pages. In my case, I needed to check if a page existed by its slug. If it did, I would update the page; if not, I would create a new one.

To achieve this, I used axios for making HTTP requests, and TypeScript to ensure type safety. But, before diving into code, I quickly realized that permissions and authentication play a crucial role in interacting with the WordPress API.

Facing the rest_cannot_create Error

When I initially attempted to create a page, I encountered the following error:

Plaintext
code: 'rest_cannot_create',
message: 'Sorry, you are not allowed to create posts as this user.',
data: { status: 401 }

The error made it clear that my user didn’t have the necessary permissions to create or update pages. This is a common problem when dealing with the WordPress REST API, as it enforces strict permission rules based on the user role.

After some investigation, I discovered that my user didn’t have the right capabilities to create posts or pages. WordPress assigns different roles with varying levels of permission. In my case, I needed to ensure that my user had either the Editor or Administrator role, as these roles are allowed to create and manage pages.

Once I switched to an Administrator user, I was able to make progress. However, using basic authentication with a username and password isn’t the most secure approach.

Secure Authentication with Application Passwords

To solve the issue of secure authentication, I switched to Application Passwords. Application passwords were introduced in WordPress 5.6 and allow secure access to the REST API without exposing the user’s main credentials.

Here’s how I set up application passwords for my WordPress user:

  1. Log in to the WordPress dashboard.
  2. Go to Users > Your Profile.
  3. Scroll down to the Application Passwords section.
  4. Enter a name for the application (e.g., API Access), and click Add New Application Password.
  5. Copy the generated password, which will be used for authentication in the API requests.

With this application password in hand, I updated my script to use it for authentication.

Writing the TypeScript Script

Now that authentication was sorted out, I could focus on the actual script to create and update pages. Here’s how the final TypeScript script looks:

TypeScript
import axios from 'axios';

interface Page {
  id?: number;
  title: string;
  content: string;
  slug: string;
  status: 'publish' | 'draft' | 'private';
}

// WordPress API base URL and credentials
const baseUrl = 'https://your-wordpress-site.com/wp-json/wp/v2/pages';
const username = 'your-username'; // Replace with your WordPress username
const appPassword = 'your-app-password'; // Replace with your generated app password

// Base64 encode credentials for Application Password Authentication
const auth = Buffer.from(`${username}:${appPassword}`).toString('base64');

// Function to create or update a page
async function createOrUpdatePage(page: Page): Promise<void> {
  try {
    // Check if the page exists by slug
    const existingPageResponse = await axios.get(`${baseUrl}?slug=${page.slug}`, {
      headers: {
        Authorization: `Basic ${auth}`,
      },
    });

    const existingPage = existingPageResponse.data[0]; // WordPress returns an array

    if (existingPage) {
      // Page exists, so update it
      const updateResponse = await axios.post(
        `${baseUrl}/${existingPage.id}`,
        {
          title: page.title,
          content: page.content,
          status: page.status,
        },
        {
          headers: {
            Authorization: `Basic ${auth}`,
          },
        }
      );
      console.log(`Page updated: ${updateResponse.data.link}`);
    } else {
      // Page does not exist, so create it
      const createResponse = await axios.post(
        baseUrl,
        {
          title: page.title,
          content: page.content,
          slug: page.slug,
          status: page.status,
        },
        {
          headers: {
            Authorization: `Basic ${auth}`,
          },
        }
      );
      console.log(`Page created: ${createResponse.data.link}`);
    }
  } catch (error) {
    console.error('Error creating or updating page:', error.response?.data || error.message);
  }
}

// Example usage
const pages: Page[] = [
  {
    title: 'About Us',
    content: '<p>This is the about page content</p>',
    slug: 'about-us',
    status: 'publish',
  },
  {
    title: 'Contact Us',
    content: '<p>This is the contact page content</p>',
    slug: 'contact-us',
    status: 'publish',
  },
];

pages.forEach(async (page) => {
  await createOrUpdatePage(page);
});

In the script, I define a Page interface to represent the structure of a WordPress page. I also include fields like title, content, slug, and status. These fields will be passed to the WordPress API when creating or updating a page.

Key Steps Explained

  1. Authentication: I use the application password for Basic Authentication by encoding it along with the username. This ensures secure access to the WordPress REST API.
  2. Checking if the Page Exists: Before creating a new page, the script checks if a page with the given slug already exists using the /wp/v2/pages?slug=your-page-slug endpoint. If it does, we proceed to update it.
  3. Updating or Creating Pages: If the page exists, I send a POST request to update it by its ID. If not, I create a new page using a POST request to the /wp/v2/pages endpoint.
  4. Error Handling: The script includes basic error handling to catch any issues with the API requests, logging errors for debugging purposes.

Final Thoughts

This script allowed me to automate the creation and updating of WordPress pages efficiently. By switching to application passwords, I ensured secure API access and resolved the permission issues I initially faced. This approach can be further extended to handle additional tasks such as managing posts, custom post types, or media.

In my experience, WordPress’s REST API is a powerful tool when working on integrations, and TypeScript adds an extra layer of safety by ensuring we interact with the API correctly. Whether you’re building a full-fledged integration or simply automating page management, this solution can save you significant time and effort.

Share this:

Leave a Reply