While I really enjoy building frontend applications using React, my work usually requires building backend apps. Most of the time, I am required to use Wordpress as my backend. In this article, I want to talk about building Wordpress applications that are maintainable. The following items are what I will cover in this article:

Setting up an environment with ease of deployment in mind

Using WP-API for building RESTful API

First things first — the environment

The WP structure consists of four main directories: root directory, wp-content, wp-admin, and wp-includes. When building a WP application, the first rule of thumb is to not store anything in anywhere but wp-content. So, I am going to take this idea further and suggest that we keep our entire codebase (e.g GIT repository) to the contents of wp-content. Usually, I create my Wordpress instances using Docker, so my wordpress installation and configuration comes from the Wordpress docker image. And cloning my repository right into wp-content makes the whole development experience easier.

Before we start, we need a namespace

What is a namespace in my context? I think of namespace as a way to isolate my codebase from WP Core and 3rd party plugins. For the sake of this article, let’s assume that we are building a backend for browsing fruits. In this case, all our “root” files will have fruits- prefix. Now that we have a base, on what I mean by namespace, let’s take this idea further and add PHP Namespace to our codebase. We will separate our codebase into 3 parts — Core, Plugins, and Theme. For each of these parts we will use namespaces in files in the following way:

<?php /* Core */ namespace Fruits\Core\CustomPostTypes; ?> <?php /* Plugins */ namespace Fruits\Plugins\OurCustomPlugin; ?> <?php /* Theme */ namespace Fruits\Themes\ThemeOne; ?>

Must-Use Plugins

Now that we explained where our codebase is going to be, let’s dive into how we are going to structure our application. Usually, I keep all the Core files inside mu-plugins defined as Must-Use Plugins. This is not the most popular opinion in WP community; however, if you look into source code on how mu-plugins and plugins are loaded, you will see that there is nothing special about mu-plugins — they just load first and there is no need for database checks because they are always loaded. There are two caveats for mu-plugins — there is no order on how they are being loaded and only the files in root directory of mu-plugins will be loaded.

What is Core and what is stored there? In order to explain Core, let’s compare Core to Plugins. Plugins are modular extensions that one can easily disable without breaking anything in the application while Core logic is what is needed for the application to function properly. In short words, everything depends on Core. That’s why it is logical to put them in Must-Use Plugins — we must not have an option to disable them. Usually, I like storing custom post types, WP-API interfaces, and some admin related functionality that is essential for my application.

Now that you have an idea on my premise, let’s create our mu-plugins directory:

mu-plugins/fruits-core.php

mu-plugins/fruits-cpt/...

mu-plugins/fruits-rest/...

mu-plugins/fruits-admin/...

This is how we will utilize our namespaces. There is no need to add a prefix for files inside namespaced directories. The fruits-core.php will include all the files inside fruits-cpt, fruits-rest, and fruits-admin. It will do in any order we mention. This way mu-plugins load fruits-core.php and this file loads all the needed files from inside directories. This is because, as I mentioned earlier, mu-plugins do not load files inside directories.

If you need mu-plugins to use a functionality from a plugin, I recommend using actions from plugins, if these actions do not exist, then use the plugins_loaded action and add use the functionality in the action callback.

Composer and external dependencies (plugins)

Now that we know how we will work with core, it is time to keep our repository clear from 3rd party dependencies. We do not need these dependencies stored in our repository; we only need them when we deploy them. How are we going to do it? The answer is Composer. Composer is a dependency manager for PHP. It allows installing 3rd party PHP libraries into any PHP based application and loading those libraries using a single include. However, composer works through a service called packagist and this service does not include WP plugins or themes. There is a service called wpackagist that allows installing WP plugins through composer. For the sake of this tutorial, we will install the following plugins: Carbon Fields, qTranslate-X, and Carbon Fields: qTranslate (Disclaimer: I have writen this plugin). Now, let’s initiate our composer configuration file. Open terminal and type the following from inside wp-content :

composer init

Once you go through the interactive initializer, you will have a composer.json file created. Open it with your favorite text editor and add the following at the end of root JSON object:

"repositories":[

{

"type":"composer",

"url":"https://wpackagist.org"

}

],

"extra": {

"installer-paths": {

"plugins/{$name}/": ["type:wordpres-plugin"],

"themes/{$name}/": ["type:wordpress-theme"]

}

}

The “extra” field object is needed because our entire codebase is in wp-content. If you added composer file in the root directory of WP you won’t need those installer paths. In our case, it is necessary because we our composer file is in wp-content. Now that we added our lines, open terminal and command the following lines:

composer require qtranslate-x

composer require carbon-fields

composer require carbon-fields-qtranslate

After installation is complete, you there will be a composer.lock file generated and the composer.json file is updated. If you check plugins directory, you will see that the plugins are now installed to their correct path.

Additionally, sometimes our WP application might require some non-WP related libraries. If you install these libraries, you will see that they will get installed into vendor directory, which is a default directory for composer dependencies. In order to load those files, I recommend adding the following line at the beginning of mu-plugins/fruits-core.php :

<?php

/**

* Plugin Name: Fruits Core

* Description: Fruits Core Functionality

* Version: 1.0

*/ // Composer Autoloader

require_once( __DIR__ . '/../vendor/autoload.php' ); // rest of the includes...



Now we can use any 3rd party dependency that we install in the future without needing worry about them getting loaded.

GIT

Once we have all these things set up, we need to keep our code repository as clean as possible. I usually use GIT, so it is time to talk about what to ignore from our repository using gitignore.

First important thing is to not ignore plugins, mu-plugins, and themes directories:

!mu-plugins

!plugins

!themes

“!” at the beginning of the line means DO NOT IGNORE. Once we have set to not ignore directories, we need to ignore everything inside all directories. I will explain the reason in a minute:

mu-plugins/*

plugins/*

themes/*

If you remember, we have talked about having a namespace and prefixing all app files using namespaces. In this article, we used fruits- prefix for our root files. So, now let’s NOT ignore files that are in our prefixes:

mu-plugins/fruits-*

plugins/fruits-*

plugins/fruits-*

Now all files and directories that start with fruits- will be in our repository. If you remember, we have talked about composer’s vendor directory. Even if vendors are empty, the directory will be created. So, we need to ignore that also. Plus, let’s ignore WP uploads directory because we do not need uploaded content:

vendor/

uploads/

The RESTful API

As we know, best way to communicate with external services for Single Page Applications is to connect them through an API. Nowadays, there are great libraries such as GraphQL for communicating with frontend libraries such as React; however, Wordpress does not support GraphQL. Therefore, we will be using the good old REST interface using WP-API, which is part of Wordpress Core since 4.7. From my experience, I only needed to work with the following filters, actions, and functions:

filter: rest_pre_dispatch

filter: rest_index

filter: rest_endpoints

action: rest_api_init

function: register_rest_route

rest_api_init combined with registe_rest_route is used for adding custom endpoints for the REST interface.

combined with is used for adding custom endpoints for the REST interface. For multilingual applications, I always request multilingual content through Accept-Language request header. rest_pre_dispatch allows me to do preprocessings such as setting the language before my endpoints start getting called.

request header. allows me to do preprocessings such as setting the language before my endpoints start getting called. WP-API adds its own set of namespaces and also adds an index endpoint that shows all the routes for the API. Since, I do not want to have documentation to my API and also like building my all my REST endpoints, I want to remove wp/v2 and the index endpoint. rest_index and rest_endpoints are the filters that match my need.

For more details regarding WP-API, I recommend checking out the WP REST API Handbook.

Conclusion

Overall, using this kind of project design has helped me achieve a more pleasant developing experience. As you know most of the websites in the world are being run using WP. This means more people are familiar with WP, which results in more clients desire for WP.

Even if this was a pleasant experience to setup and develop WP backends that connect to frontend apps like React, I personally like building these kind of backends using frameworks such as Django (Python), Laravel (PHP), Express (Javascript) as they have better support for internalization, building REST interfaces and GraphQL schemas and many more.

Additionally, I have also created a project that showcases all the features and naming conventions used in this article: https://github.com/appristas/example-wp-project

For any questions, suggestions, or discussions regarding this article or the project leave a comment here.