Guide: Build a RESTful API microservice with PHP

Too many times I’ve seen people lost in the weeds arguing about endpoints, design patterns, reusability, etc. It turns out – with a decent microservice – the architecture lends itself to simplicity. For this guide – I’ll take you step by step on how to standup a minimal Restful API Microservice using PHP. I’ll be using the Waryway Microserver Engine.

Setup the Project

This guide won’t cover adding your project to GIT.

  • Create your project directory
  • Add a composer.json file
  • Add the dependencies
    • waryway/micro-service-engine
    • waryway/php-traits-library
  • Add the autoloader to the composer.json
  • Add the src directory
"autoload": {
  "psr-4": {
    "OrgName\\ImagerServerRest\\": "src/"

At this point, your project is going to look something like this:


Create an initial endpoint

For the initial endpoing, I’m using the example from the

  1. Copy the Router.php from example
  2. Replace Waryway\example with OrgName\ImageServerRest
  3. Change the setRoute to only ‘/’ and respond with ‘Hello World
    • $this->setRoute(['GET', 'POST', 'PUT', 'DELETE'], '/', [__CLASS__,'helloWorld']);
    • This was mostly just to provide a ‘test’ endpoint.
  4. Run composer update
  5. Run vendor\bin\server OrgName\ImageServerRest
  6. In a browser, navigate to localhost:99
Tada! You got a ‘Hello World’ response.

Add a real endpoint

The real cool thing. Adding an endpoint. If you ever want to add an endpoint, make certain to use a proper router. Frameworks have them. I like nikic’s fastrouter in PHP, or Mux is also fine in Golang.

Not getting too fancy – I just add a new route (/images), and a route handler (getImageStatistics) – and set them up to return according to what I find out. The example itself gets rather long – so I’ll simply link to it in github. Once I’ve gotten all the changes in place, I can run vendor\bin\server.bat orgname\ImageServerRest and try out the URL. Eureka! now returns a list of information about the available images.

Make it Conventional

It’s one thing to build an endpoint. It’s another to publish an endpoint. What I’m saying is this: If all lightbulbs had different size sockets, it would be much harder to go to the store to buy lightbulbs. In the same way – if all RESTful api’s have a similar standard for definition to clients, they become much simpler to use overall. The OpenAPI Specification provides just that, and Zircote/Swagger-PHP is a great tool to implement it.

Start by updating the composer.json with the swagger-php composer setting as a dev require. Then run composer update. Who knows, maybe Waryway will add it to the microservice engine at some point.

"require-dev": {
  "zircote/swagger-php": "dev-master"

Using the documentation from Zircote is pretty straightforward and the annotations are pretty obvious. That said – I ended up using this command to generate a nice little swagger.json file:

vendor\bin\openapi.bat --pattern "*.php" --output "./src/swagger.json" --format json ./src

The easiest way to see if the generated file works correctly, is to use the swagger editor, and paste the file contents into it. The editor will automatically convert any json to yaml – and will nicely update the generate API documentation. It’s really neat!

example of

Another cool trick with swagger, is the simplicity of adding the swagger view into the codebase itself, and exposing the entire utility via an endpoint. For this guide, I’m simply adding the swagger doc to view it at /swagger.json

Wrap the Package

If you thought containers weren’t going to make an appearance while talking about microservices, you should have paid for insurance. If you are considering the cardboard boxes, plastic totes, or even shipping containers – you might want to brush up on Docker!

The first piece of wrapping a package, is to take a step back from the /src directory and create a /build directory. The goal here, is that the build directory never actual lives where the program is deployed. Once you’ve added the build directory – let’s add a Dockerfile. Now, it doesn’t have to be a fancy Dockerfile – at least not for getting started. I am going to use the 7.3.10-cli-alpine from Docker Hub as a base image.

Note: The only files you really want to work with are your composer.json, composer.lock, and src directory.

There are many different flavors of docker files. For this one, I’m not really optimizing for size. I don’t clear the composer cache, for example. Notice however, that the swagger.json generator is included in the build. I also added the build command I used as a comment.

FROM php:7.3.10-cli-alpine

RUN curl -sS | php -- --install-dir=/usr/bin --filename=composer
COPY ./composer.json ./composer.json
COPY ./composer.lock ./composer.lock
RUN composer install
COPY ./src ./src
RUN composer dump-autoload
RUN mkdir -p /assets/images
RUN ./vendor/bin/openapi --pattern "*.php" --output "./src/swagger.json" --format json ./src

CMD php /vendor/bin/server orgname\\ImageServerRest

# docker build -t orgname/image-server:v1 -f build\Dockerfile .

Once you’ve gotten the Dockerfile built – simply kick it off, and remember to mount the ‘images’, or else there will be none.

docker run -it -p 80:80 -v C:/development/ImageServerRest/assets/images:/assets/images --name test orgname/image-server:v1

What now?

Well, now we’ve built a microservice, put it in a container, defined it with swagger, and even got it up and running locally – the next logical step is to ship the container! You’ll need to harden it (because containers are best not run as root). You’ll need to get it to a registry (dockerhub is my favorite currently). CICD for the entire project would be a huge boon… And, of course! you’ll probably want to write a different endpoint then my contrived ‘image stats’ endpoint.

I hope you’ve enjoyed this guide. If you have any problems following it – feel free to comment and ask about it! If there is a bug in the code – a new issue on GitHub would be greatly appreciated. If you’d like more guides like this: like, subscribe, and comment! Finally, thanks for reading!