Categories
Software Development

WordPress REST API Snafus

While migrating data I was forced into evaluating whether using the CLI or REST API would be more effective. If I was smart I would have documented the difficulties I discovered while investigating, but since I didn’t, this is a rough approximation of all the issues I found (or caused).

It all started with a migration I’m running on my wife’s blog. The site I’m migrating used an old PHP gallery system and I’m attempting to import all the imagery into the stock WordPress media library. Because the migration from custom plugins has been painful, the changes need to be as future-proof as possible to prevent the same thing from happening again. The only current custom code I’m using is implementing a custom taxonomy tagging the media with an “album” name.

So my migration process is to: (✅ are done)

  • ✅ scan a post
  • ✅ generate a list of the images that need to be migrated
  • 🔴 upload the images
  • 🔴 Attach to the post (and hopefully tag them with the correct album).
  • A final step of verifying the new content needs to be done manually just because I want to make sure everything looks good for each post.

First attempt: CLI

Locally you can use the WP-CLI tool to upload media to your site quite easily. If you connect to your installation remotely (i.e. wp --ssh=<blah>), this doesn’t work unless you first upload the media to a staging area, use the remote system paths and then remotely clean your staging area.

# Example (--porcelain returns the post_id of the new media item)
wp media import <path-to-file> --post_id=$POST_ID --porcelain

Staging the images would have been the quickest method, and I played with the idea of a shell script, but as I thought about it, I thought using the API would be cleaner (and it should be rock solid since it’s been in use for a couple years all the pain points have probably been smoothed out and answered online, right?).

Using the REST API

Prototyping should be quick, just fire up Postman or use curl to get started. Of course before you can do anything really interesting you need to authenticate yourself. But passing your password every request seems like a recipe for disaster. There has to be a better way!

OAuth

There are a couple plugins that add OAuth (with different flavors and free/pro offerings). Since I wanted as close to the “stock” experience as possible I went with the 1.0a implementation of the core WordPress REST team.

I generated my tokens and keys using Python as the existing OAuth clients for WordPress wouldn’t work for me and don’t seem to be updated to work with newer versions of PHP/composer. (Is this entire system even supported anymore? The WP-CLI client github project was archived in 2019!)

I then spent (not joking) at least 2 hours trying to get the undocumented media endpoint working. Seriously, look at the official documentation and try to tell me how you send the file. These auto-generated docs are missing a lot of information that I had to discover in forum posts and through reading the code.

Eventually I came to the conclusion that I was calling the endpoint correctly (Reader, use a multipart/file POST with the field named ‘file’ — it doesn’t accept multiple files — other parameters as documented above do work. Unfortunately I was also looking at the arguments returned from the REST options call which are NOT fully supported.)

The problem actually lied in the authentication mechanism.

App Specific passwords

Just use this, or you can dive into the OAuth server plugin and fix that… (Please only use if you have HTTPS enabled).

What’s left

Authentication sorted, I dove into the issue of my custom taxonomy which while actually listed in the REST API endpoint as a valid argument, but actually doesn’t do anything. Again, punting here, I assume it’s a loading issue with the taxonomy code, but I didn’t want to dive too deep and I already spent too much time on this. I’ll just send another REST call adding the taxonomy to the post ID.

REST API example

Conclusion

If I could send a message back in time I would probably tell myself to just deal with the CLI method. But since I’ve done so much investigation I’ll probably implement a version of the REST API implementation as it’s more flexible than trying to parse some of the CLI commands (which can have elements oddly named and under odd sub-commands with options that might or might not be documented).