How to get WordPress REST API self-requests working in a local development environment, i.e. Docker

I’ve only just started using Docker to run a local development environment for testing my WordPress plugin development. (Yeah, don’t get started. I’ve been working successfully for nearly 3 decades by testing my code on a remote development server.)

Using Docker is great! It’s so much faster to be able to just save my code and hit refresh to see the changes immediately, without having to sync the changes to the server… and local load times are lightning-fast!

But there’s a problem. ICS Calendar Pro‘s main functionality is to retrieve and process data from external URLs (specifically iCalendar subscription feeds), using the wp_remote_get() function. Once I added the ability to create and manage similar data locally, the obvious solution to me was to still retrieve and process that local data as if it were an external source, so ICS Calendar Pro generates ICS feeds for the internal event data, and then it makes wp_remote_get() requests to itself.

This was not working in Docker, and it took me several hours of wading through a sea of AI slop on the WordPress “how-to” websites that have spread across the web like a film of toxic algae, to finally find an answer.

Port forwarding.

I access the local Docker site at the URL http://localhost:8888 but it didn’t occur to me immediately that since Docker is running a virtualized Linux system inside a container, it’s Docker that is listening on port 8888 of my Mac system, but then Docker is routing those requests to Apache listening on port 80 inside the container.

Since the plugin’s REST API requests are coming from inside the container, Apache inside the container also needs to be listening on the port Docker is using outside the container.

This involved going into the file system of the container within Docker and editing the /etc/apache2/ports.conf file, to add the second line shown below:

Listen 80
Listen 8888

I restarted the container and boom! Everything was working.

Well… I think that’s because I had already taken another step that was probably essential for this. Since the WP REST API is using URLs with the wp-json/ path, WordPress’s URL routing needs to be activated. By default, the WordPress installation in the Docker container had the Permalink structure set to “Plain.” Changing that to anything else (on the Settings → Permalinks page) makes the REST URLs work.

Building a custom WordPress block without a React/JSX build process

I spent two solid days trying to make this work. And it’s not purely because I’m too stubborn to learn React/JSX. That’s part of it.

It’s more that I’m too stubborn to introduce an arbitrary build process into my development workflow. I have my own idiosyncratic ways of doing things, and I’m coding solo, so I generally don’t need to conform to someone else’s standard practices. As long as what I’m doing has its own internal consistency and safeguards, and produces a reliable product, who cares how I get there?

I actually have introduced a few “build” processes into my own workflow, using Git (out of necessity) for version control of my WordPress Plugin Directory plugins, my own custom versioning process for non-repository plugins, and “minifying” all of my JavaScript and CSS (using online minifiers).

But I am not doing any significant amount of block development. Certainly not enough to learn JSX and implement the arbitrary build process into my workflow, adding thousands of unnecessary dependency files to my development codebase, etc.

For the latest version of ICS Calendar Pro (version 6), currently in development, I just needed to add a simple custom block that lets users insert a calendar (a Custom Post Type) by selecting one from a dropdown list. It’s a more user-friendly option than what they’ve had to do up to this point: copying a small code snippet from the CPT’s admin page and pasting it into a Shortcode block.

Ideally my new block would render a preview of the calendar in the Block Editor, but that’s secondary to just having a way to insert the block itself and pick from the list of saved calendars.

I found the official list of block development examples, and made some progress with the “no build” example. I also found a great article from Cello Expressions (maker of the Sheet Music Library plugin), called Dynamic Blocks with Block.json, Vanilla JS, and No Build Process. I really thought that was going to get me exactly what I needed… until I realized that their particular needs did not involve having any kind of configuration options in the Block Editor itself.

Then I found DahmaniAdame/block.js on GitHub Gist. It was exactly what I needed. A straightforward, no-build example, using Vanilla JavaScript, of a simple block with a dropdown list of posts in the inspector sidebar.

The only problem: it worked perfectly if I stuck with built-in post types, but it would cause a vaguely defined block error when I switched it to using my CPT.

Eventually I realized the problem was here:

const title = post.meta.some_meta1 && post.meta.some_meta1 !== ''
		? post.meta.some_meta1
		: post.title.rendered;

For an as-yet undetermined reason, I’m not able to retrieve meta data about my CPT. (Yes, I changed some_meta1 to the actual key of my meta data field, view.) I decided I didn’t really need to retrieve that meta data, so I just removed the conditional, i.e.:

const title = post.title.rendered;

With that change (and a few others to change the post type I’m querying and remove a few other things I didn’t really need), my block was working perfectly! So much so, in fact, that I decided to try to push it to do more things. There, I ran into some trouble, because my block does a lot of JavaScript stuff when it renders, and I haven’t (yet) figured out how to get that JavaScript to run when the content renders in the Block Editor, so the HTML output of the block does get inserted into the Block Editor when the user selects from the dropdown, but the JavaScript that actually reveals it on the front-end page isn’t running. (Yes, I used enqueue_block_editor_assets to load my JavaScript and CSS files. And I also added logic to try to force my plugin’s JavaScript initialization event to trigger, but something more complicated is going on.)

Anyway… it’s still not 100% perfect, but I really wish I had found that Gist before I spent two full days banging my head against the wall over this!

Two-factor authentication is not the solution to the inherent flaws of password-based security

Uh, I really don’t have much more to say than that.

OK, maybe a bit. As a web developer working in client services, at least once a week I am confronted with the situation of having to log into a client’s account for something… MailChimp, GoDaddy, etc.

Many of these services have switched to 2FA-by-default, which I agree is more secure than plain old passwords (which I bet some of them still store in their databases as clear text). But 2FA is a pain in the ass. Especially when you’re in my position, and the phone number or email address that receives the one-time authorization code belongs to the client, not me.

Any time I need to log in, it requires coordination with the client to be sure they’re available to pass along the code to me. Which is just stupid.

Fortunately a lot of these companies have realized how common this kind of situation is, and how it’s a valid scenario, and they’ve worked around the limitation by creating “teams,” so clients can add me to their account as my own separate user, with my own login credentials, and my own 2FA.

But it’s still a pain in the ass. And not every service offers it. For example, MailChimp used to allow up to 3 users, I believe, on their free accounts, but now it’s just one. Of course, of course. Just pay for the service, right? Well sure, but service providers with a free tier imposing such a ridiculous limitation on that free tier as a way to upsell the paid tiers is kind of self-defeating. “Hi, we’re creating a crappy experience for you, and that’s the only experience you’ve known with us. But if you start paying us, we’ll make it not-crappy. We promise!” OK.

But it’s not really MailChimp’s fault. It’s that 2FA sucks. It’s more secure than plain ol’ passwords, but it’s even less convenient.

And while I’m ranting futilely, why do we even need security at all? Because people suck, period.

While I was writing this, I was waiting for a client to send me a 2FA for MailChimp. I’m in! And fortunately, this particular client is on the paid tier, so I was able to add myself as a user. A process which involved… wait for it… a CAPTCHA! (Time for another rant.)

ST:TNG Treadmill Review #2: The Schizoid Man

The Schizoid Man
Season 2 Episode 6
Original airdate: January 21, 1989

Netflix Synopsis

The Enterprise responds to a request for medical assistance from Dr. Ira Graves, considered by many to be the greatest living mind in the universe.

My Brief Review

I skipped a few episodes between yesterday and today. Data on the Holodeck as Sherlock Holmes? No thanks. Two episodes in a row where the Enterprise has to mediate between warring factions on some random planet? Yawn. An episode with a title that references King Crimson? Hell yeah!

This is quintessential TNG. Data, longing to be human, begins the episode by modeling his new Riker-inspired beard to Geordi and Troi. Eventually the crew ends up on a planet occupied only by a dying mad scientist and his young assistant. And guess what? The scientist is the guy who taught Data’s maker cybernetics. You can see where this is going, can’t you? It’s hard to believe it took the crew more than five seconds to figure out why Data returned to the ship with a newfound swagger and insubordination.

Memorable Moment

This was an episode that followed a familiar pattern for me: most of it was only vaguely familiar, since the premise of just about any Star Trek episode is vaguely familiar when you’ve seen so many of them, but then there were moments that would pop out of nowhere, like when Data begins delivering an over-the-top eulogy for the deceased Dr. Graves (“to know him is to love him is to know him”). As the crew begin looking around at each other with bemusement, and Captain Picard interrupts Data, I remembered distinctly what comes next: “I’m almost finished, sir.” “You are finished, Mr. Data.”

Crew Rando

My memory of TNG is that there are very, very few Vulcans in the series. Personally I’d much rather have more Vulcans and fewer Ferengi. But here we have a rando Vulcan in a prominent role in the episode! Lt. Selar is the doctor sent with the away team, because Dr. Pulaski had to go with the rest of the crew to rescue passengers on a damaged ship, a plot device that barely figures in the episode, but I suspect it owes to the fact that Diana Muldaur, as Dr. Pulaski, was not young and attractive enough to figure in one early scene where the leering, sexist Dr. Graves comments repeatedly on her appearance. So we get a young and attractive Vulcan doctor, who never existed before or after this episode. Hmmm. I kind of wish I hadn’t thought this through, because it diminishes my appreciation of the episode. Still… a Vulcan! On TNG!

Distance Rating: 5K

IMDb score: 7.0/10

ST:TNG Treadmill Review #1: Where Silence Has Lease

Where Silence Has Lease
Season 2 Episode 2
Original airdate: November 28, 1988

Netflix Synopsis

The enterprise encounters a mysterious void in space and when they move in closer to investigate further, it envelops them and they can’t get out.

My Brief Review

This is a classic Star Trek scenario, and one of my favorite types of episodes: a spatial anomaly where the crew has to confront the unknown. It actually ended up being slightly disappointing to me though because it was almost too predictable… it went into territory tread heavily both in the original series and in subsequent installments, plus, in the context of TNG, it felt too much like a scenario Q would put them in (and already had by this point). Bonus points for an immortal, formless space being taking on a semi-human appearance in order to interact with the crew. Classic Trek.

Memorable Moment

After Riker and Worf beam over to what appears to be Enterprise’s sister ship, the USS Yamato, they find themselves in a surreal moment where the turbolift door from the bridge leads into a mirror image of the same bridge, and Worf loses it. “A ship has one bridge. One bridge!!!”

Crew Rando

We get a true “red shirt” moment, which TNG seemed specifically designed not to allow after swapping the use of red and gold uniforms between command and operations crew, when the immortal, formless space being kills helmsman Lt. Haskell (who?)… a red shirt!

Distance Rating: 4K

IMDb score: 7.1/10