Using WP REST-API and Angular Universal as a wordpress workaround – Part 1

Since I was pretty lazy about my blog in the last years, I wanted to start a new series (hopefully) about a private project which has been online for about 2,5 years.

WordPress has become a standard for smaller and even bigger websites as a CMS or blog system. I faced the challenge to work with wordpress even though I am not the biggest fan and would never use if I had a free choice. Anyway I got a more private project with the following acceptance criteria.

Acceptance criteria on a project level

  1. Use wordpress, since this is the preferred customers choice
  2. Good SEO of the site
  3. Low maintance costs

Since this is a private project I also had some acceptance criteria.

Acceptance criteria on a personal level

  1. Want to have as little contact with wordpress as possible
  2. New technology
  3. Easy to deploy

Using the WordPress Rest-API

After some research I discovered the WP Rest API. For me this was a wonderful use since I don’t have to handle wordpress themes. It comes quite natural to use a SPA (Single Page Application), like Angular, Vue or React. The problem with SPA’s is that they are not really good when it comes to search engines, since SPA are rendered in the browser. So I discovered that angular offers a library for ServerSideRendering called Angular Univeral, which executes on a node server and renders static pages, that later get bootstrapped on the client. 
So all I need was a node server, I took pm2 and nginx as http server.
so I got this little nice setup which can run on one AWS ec2 micro instance and even use the free tier in the first year.

CI/CD Pipeline

Having a basic architecture the next challenge was the deployment.
Since the WordPress blog was running on a simple webhosting space and for simplicity I decided to leave it there. I only had to take care for Nginx and the angular app.

My prefered choice for CI/CD Pipeline is currently GitLabCi. So I decided to have two docker container, one for the nginx and one for the angular app.

For the nginx container it is obvisouly very simple, it just builds and pushes in the docker repository.
Since I have two container I use docker-compose in order to orchestrate them.

For the moment I decided to have a separate CI/CD pipeline just for the deployment, I just change the versions manually in the docker-compose.yml, run the pipeline and only deploy the docker-compose.yml file and restart the container on the ec2 instance, after that I run my cypress e2e tests, to make sure everything works. There are probably better and more automated solutions for that but right now I fine it.

So finally I have a pretty nice solution for working with a wordpress site without any wordpress hazzle. The site is fast easy to maintain and scalable, and all this with very low maintenance costs.

Rest API – Good Practises

Da REST API in letzter Zeit recht populär geworden sind habe ich ein paar grundlegende Richtlinien für gutes REST API Design zusammen getragen.

Resources & Operationen

Eine Resource ist eine Entität auf der verschieden Operationen ausgeführt werden könne.
Eine Resource repräsentiert ein Objekt wie User, Adressen, Logeinträge.

Folgende Besipiel zeigt die Operationen auf eine User Resource:

GET         /users/      – holt alle User
POST      /users/       – erzeugt einen neuen User
PUT        /users/{id} – ändert die bestehende Resource
GET        /users/{id} – hole die Resource eines Users
DELETE  /users/{id} – löscht die Resource eines Users

Eine Resource kann Sub Resourcen haben:

/users/{id}/adresses/{id}

Parameter

URL Parameter
Normalerweise der Pfad zur Resource.

URL Query Parameter
Für einfach Parameter, in der Regel Filter oder Paginierung auf GET Anfragen, z.b. um eine Collection zu filtern.

HTTP Body Parameter
Auch Payload genannt, um JSON Daten zu übergeben, über POST und PUT hier wird dann im Header der Content-Type: application/json übergeben.

HTTP Header
Werden in der Regel für Token oder CorelationId un ähnliches verwenden, nicht aber für Resourcen.

Bennenung von Resourcen

  • Resourcen sind immer im Plural
  • Hierachien werden durch Slashes “/” getrennt
  • Kleingeschreibung ist bevorzugt
  • “-” sollen verwendet werden und kein  “_”
  • keine File Endungen in URL
  • Eine Resource ist IMMER ein Nomen und NIE ein Verb.

URL Struktur

Adresse
http://hostame.de

Base Path
/produktName/api/ oder nur api/

Resource Path
/users/

Fehlerhandling

Fehler werden in der Regel mit http Status Codes behandelt, ein richtiger Umgang mit diesen Status Codes erleichtert dem Konsumenten
der Umgang mit der API. Hier eine lIst der gebräuchlichsten Error Codes:

200 OK
201 Created
202 Accepted
204 No Content
400 Bad Request
401 Unauthorized
403 Forbidden
404 Not Found
409 Conflict

Wem das nicht reicht findet hier eine komplette Liste:
https://de.wikipedia.org/wiki/HTTP-Statuscode

Zusätzlich zum Error Code sollte eine JSON Message zurückgeben.

{
"File not found": "User does not exist"
}

HATEOS

Hypermedia as the Engine of Application State
in ein Prinzip, das auf Hyperlinks zur besseren Navigation durch die API setzt.

{
   "id":12,
   "name":"Torsten",
   "adresses":[
      {
         "id":"23",
         "street":"Meine Strasse 12",
         "links":[
            {
               "rel":"self",
               "href":"/api/users/12/adresses/23"
            }
         ]
      }
   ]
}

Man muss meiner Meinung nach allerdings sagen, dass dieses Konzept eher eine akademisches ist und in der Praxis eher hinderlich ist als das es hilft. Die Implementierung ist sowohl auf Client wie auf Server Seite deutlich aufwändiger ohne einen wirklichen Nutzen zu haben.

Controller/Action

Da die meisten Anwendungen nicht rein Resourcen orientiert sind oder sein können, sollten Operation, die nicht einer Resource entsprechen
deutlich kenntlich gemmacht werden.
Man kann Verben verenden um klar zu machen das ist keine Resource:

  • validateUser
  • calculateLoanCondition
  • activeAllUsers

Diese sollten dann der entprechende Resource zugeordnet werden und immer POST verwenden:

POST /users/validateUser