Use Lumen as API Framework

Disclaimer: we at Cloudoki think profoundly API-centric. The API is what defines your project. By upgrading the API to the center piece of your application environment, you by definition improve on security, scalability, extensibility and documentary.

That being stipulated, let's.

RESTful API

A RESTful API (Representational State Transfer based Application Programming Interface) is mainly a general category describing a HTTP based CRUD (GET, POST, PUT/PATCH, DELETE) method interface.
The sweet side of an API is the containable authentication environment and a maintainable routes/endpoints based documentation overview. And it lives by a set of simple, object oriented rules.
As a rather juicy cherry on the cake, you're developing a structure you use yourself that is easy open for 3d party developers without any changes.

Lumen

Lumen is Laravel's latest prodigy. In their words:

The stunningly fast micro-framework by Laravel.

Basicly, Lumen is a boiled down version of Laravel, on it's turn a modern execution of Symfony2. All in all a well documented, matured framework with a huge community. For PHP standards the best you can get. If you come from Python (probably not), it resembles Pyramid a bit in an MVC way.

Start

Let's begin with setting up our local environment and package dependencies.
We'll be needing Nginx, Composer and Lumen. The Set up you local battleground article will assist you.

Lumen install

The simple lumen new command will create a fresh Lumen installation in the directory you specify. This method of installation is even faster than installing via Composer.
Move to your favorite location to create the project folder. For this article, we’ll use ~/api as reference. You'll also have to alter permissions on app/storage.

$ cd ~
$ lumen new api

$ mkdir api/app/storage
$ sudo chmod -R g+w api/app/storage

Application ready! Build something amazing.

Local url

To view our pretty, new Lumen project, we need to set up a new vhost. If you are new to this, go to our Nginx article, you can use the Laravel docs for Lumen as well.

Lumen You have reached your destination.

Env

Let's take a look on the new style .env usage in Lumen. First copy the example - note that the .env file is git ignored in ... .gitignore (a welcome addition).

$ cd api
$ cp .env.example .env
$ nano .env

The first change you might want to perform is adding a random 32 char long string as APP_KEY.
Let's add a version tag too APP_VERSION=1

Routes

Now that we have a pretty Lumen view, let us set up some Routes.

Versions

This is a demo API, still you should implement some basic version sanity. Because it's a drag if you forget in real world projects.

Let's add a routes folder and a version 1 file. You'll want to include the custom routes file, and add some system actions.

$ cd app/Http
$ mkdir routes
$ nano routes.php 

# System
$app->get('version', 'App\Http\Controllers\ApiController@apiversion');

/**
 *    Version 1 routes
 */
include "routes/routes-v1.php";  

The /version endpoint returns the current default API version.

$ nano routes/routes-v1.php

<?php  
/**
 *  Guest endpoints. No OAuth2 required
 */
$app->group(['prefix' => '1'], function($app)
{
    # System
    $app->get('ping', 'App\Http\Controllers\ApiController@ping');
});

Add the tester /ping endpoint.

As you can see, we use a controller for processing. The Lumen install comes with an example Controller, you can rename it if you feel like.

$ mv Controllers/Controller.php Controllers/ApiController.php
$ nano Controllers/ApiController.php

Alter the contents, so we return a json response containing the .env version tag, and of course a "pong".

class ApiController extends BaseController  
{
    /**
     *    Get the API version
     */
    public function apiversion ()
    {
        # Default version on config file
        return response ()->json (['version'=> env ('APP_VERSION')]);
    }

    /**
     *    Get the API beat
     */
    public function ping ()
    {
        # Default version on config file
        return response ()->json ("pong");
    }
}

To utilize the env () helper function, enable the $_ENV PHP super-global by uncommenting the call to Dotenv::load in bootstrap/app.php.

$ nano ../../bootstrap/app.php

Go ahead and take the API out for a spin.

http://localhost/version  
http://localhost/1/ping  

Now that we have a working, wonderful little API, you could stop here.
You can copy-paste controllers, add some CRUD endpoints like $app->get (), ->post (), ->patch (), ->put() and ->delete() to get things rolling.

Accessing the database is equally straight-forward. After adding your credentials in .env, you can utilise the Query Builder, or even better, Eloquent ORM.
For Eloquent, you'll have to uncomment the $app->withEloquent() call in your bootstrap/app.php file.

However! We could also up the ante by constructing an API suited for real life release by adding Message Queueing (3-layer structure) for scalability and Oauth2 for authorisation.


Message Queueing

Who doesn't love a word with with five vowels in a row?

Our parent article, 3-Layer Architecture, explains the advantages of a divided API and Worker environment. In short, the architecture improves load balancing, scalability and security.

Setting up your Worker is outside the scope of this article, so we've written a sibling post, Use Laravel as Worker Framework, that plays nice with this API.

Once you have your demo Worker set up, Let's focus on this side of the MQ story.

Install

Since we're going to use the Queue facade, be sure to uncomment the $app->withFacades() call in your bootstrap/app.php file.

To enable Foreground jobs in Lumen, you'll have to include our dependency package

"cloudoki/frontqueue":"master-dev"

Don't forget to run composer update.

Local environment

For local usage, running a MQ server and maintaining workers is exceedingly complicated and mostly useless. The Lumen synchronous mode solves this, sugared with some FrontQueue. Edit your local .env:

QUEUE_DRIVER=synchronous  
Server environment

For your server environment you'll need to install Gearman, the main supported foreground queue driver.
We've written a an article about this: Set up the Gearman MQ.

Authorisation

Lumen comes with Authorisation out of the box. However, this doesn't serve our needs, from the moment we want to split up the actual authorisation handling to the worker, and the views to the API. Oauth2 is our poison of choice for this asynchronous approach.

Oauth2 Stack

To make life easy, we created an Oauth2 Stack package. It's exceedingly powerful, yet takes only a couple of minutes to install on both of your environments.
Head to the Oauth2 Stack article.

Keep in mind that Lumen is micro, so you'll have to enable Eloquent (used by the stack) in bootstrap/app.php, simply by uncommenting // $app->withEloquent().

Lumen Bootstrapped for your convience.

Error Handling

For good measure, we prefer some coding standards and the extending of PHP's Exceptions. You might be mildly surprised, but we have a package for that as well.
Take a few minutes to read into the Except-io-nal package.
Bear in mind, you can always copy-paste what you need without using the whole package.

You might want to keep your response code variations to a minimum. Our exception package comes with 200, 400, 403 and 404 out-of-the-box.

Model endpoints

The Account and User models coming with the Oauth2 Stack are perfect to make our hands dirty and create some useful endpoints.
Let's start by figuring out which CRUD entries you might need.

The endpoints

Account. You'll want to represent a index (GET /accounts) of your existing accounts, store (POST /accounts) new ones, show (GET /accounts/id) singles based on id, update* (PATCH /accounts/id) some, and in the end *destroy *(DELETE /accounts/id) your Fubars, for super-users. Normal users access, will need the /accounts prefix.

Users. In general, Users behave like Accounts, with the exception of POST, since you'll want to relate your user to a given account. Index (GET /users) your existing users, store (POST /accounts/id/users) new ones, show (GET /users/id), update* (PATCH /users/id) and *destroy *(DELETE /users/id) single users. And for your convience: /me.

In Lumen, define the Middleware for a basic auth check first (on group), then enable the refering endoints.

$app->group(['prefix' => '1', 'middleware' => 'auth'], function($app)
{
    # Accounts
    $app->all('accounts', 'App\Http\Controllers\AccountController');
    $app->all('users/{id}/accounts', 'App\Http\Controllers\AccountController');


    # Users
    $app->all('users', 'App\Http\Controllers\UserController');
    $app->all('accounts/{id}/users', 'App\Http\Controllers\UserController');

    $app->all('me', 'App\Http\Controllers\AccountController@me'); 
});

API Controllers

As you probably noticed, we added a couple of controllers. They handle the various CRUD calls by defining the required filters and Worker actions. Both should be created in app/Http/Controllers.
The Account Controller.

$ nano Controllers/AccountController.php

class AccountController extends BaseController  
{
}

And the User Controller.

$ nano Controllers/UserController.php

class UserController extends BaseController  
{
}

Worker Controllers

Now we have the actual Worker action requests set up. Let's add the Account and User handling in the workers too.
We've created an sibling article to this one, called [Set up your Workers] (http://blog.cloudoki.com/set-up-your-workers), about setting up your Worker application.

Once your worker is all ready to go, call your fresh and crispy endpoints, you'll need to access your access_token. Once you obtained it, use an app like [POSTman] (http://postman.org) to call them.
Go ahead and take them out for a spin, this article is finished.

comments powered by Disqus