Building a RESTful API with Lumen5.5

Published Oct 20, 2017Last updated Oct 24, 2017
Building a RESTful API with Lumen5.5

In this post, we are going to learn about REST by building a RESTful API using Lumen5.5.

What we will cover:

  1. Overview of REST
  2. Installing Lumen5.5/setting up for development
  3. Creating and running migrations and seeders
  4. Implementing HTTP verbs (GET, POST, PUT, PATCH and DELETE)
  5. Unit testing with PHPUnit
  6. Hypermedia for easy API navigation
  7. Recap

Overview:

REST is an acronym for representational state transfer

  • Resource based: we talk about things or resources instead of actions or verbs, e.g Person (correct), GetPersonData (not RESTful)

  • Representations: The media type, typically JSON or XML

  • Constraints:
    => uniform interface: HTTP verbs
    => stateless: server contains no client state, any session state is held on the client

    Read more about REST constraints

Install Lumen5.5 and setting up for development

Installation

composer create-project --prefer-dist laravel/lumen my-app

As you will see further down in the tutorial, our models extend Eloquent Model. We need to inform Lumen when bootstrapping the app. Open bootstrap/app.php and uncomment the following:

$app->withEloquent();

Generate and set the app key. If the application key is not set, your user encrypted data will not be secure. To do so, open the routes/web.php file and add the function to create a random string.

$router->get('/', function () use ($router) {
    return str_random(32); // generate random string
});

Tip: I will be using this very handy command line tool, httpie, to test my endpoints. Check it out!

Simply run this on your terminal:

http the-url-to-your-app

Copy the outputted string and paste in APP_KEY in your .env file. You should also set the following config in your .env file:

DB_CONNECTION=
DB_HOST=
DB_PORT=
DB_DATABASE=
DB_USERNAME=
DB_PASSWORD=

REST is resource-based because we look at resources as things or nouns, and not as verbs or actions. Typical examples would include a Book resource or User resource. In this tutorial, we will be implementing a User resource only.

Creating and Running Migrations and Seeders

From the root of your application, run:

php artisan make:migration create_users_table

The user's migration file will be placed in the database/migrations directory in your app.

alt

Now run the migration:

php artisan migrate

Before we write our seeder class, you need to autoload your tests and database directories. Open your composer.json file and update the autoload:

"autoload": {
        "psr-4": {
            "App\\": "app/"
        },
      "classmap": [
        "tests/",
        "database/"
      ]
    },

Then run composer dumpautoload

We will be using the Model Factory method to seed our database: database/factories/ModelFactory.php

$factory->define(App\User::class, function (Faker\Generator $faker) {
    return [
        'name' => $faker->name,
        'email' => $faker->email,
        'password' => app('hash')->make('secret'),
    ];
});

database/seeds/DatabaseSeeder.php

<?php

use Illuminate\Database\Seeder;

class DatabaseSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        $this->call('UsersTableSeeder');
    }
}

database/seeds/UsersTableSeeder.php

<?php

use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;

/**
 * Class Users Table Seeder
 */
class UsersTableSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
      factory(App\User::class, 5)->create();
    }
}

We will make some modifications to the User model.

alt

  • we made the name and password fillable (so we can mass update them)
  • we added a protected property called $appends
  • we implemented a getLinksAttribute method. This method adds the URL to the newly created resource to the appended property, called links.

Now we are going to perform CRUD on the User Resource using HTTP verbs.
Open up your routes/web.php file.

alt

Notice how the URIs are named. Having a strong and consistent REST resource naming convention is important. It is, as a matter of fact, a good design decision.

Resource naming guide

app/Http/Controllers/UserController.php

alt

Test the endpoints from your terminal using httpie:

http --form post http://your-app-base-url/user name=sally email=sally@foo.com password=sally123

http get http://your-app-base-url/user/1

http --form put http://your-app-base-url/user/1 name=update password=newpassword

http --form patch http://your-app-base-url/user/1 name=newName password=mynewpassword

http delete http://your-app-base-url/user/1

http get http://your-app-base-url/users

Screen Shot 2017-10-06 at 2.01.32 AM.png

You'll get a nicely colorized and formatted terminal output with this tool. I absolutely love and recommend it.

Lumen does not support session state, therefore incoming requests that you wish to authenticate must be authenticated via a stateless mechanism such as API tokens. I recommend this post on implementing JWT in your Lumen app. It's a great article.

When you are storing/tracking states, you are not writing a RESTful API. Storing state is the business of the client and not the server when implementing RESTful APIs.

Unit testing your RESTful API

We are going to first set our testing database. Create a file named database.sqlite in your database directory. Open up your phpunit.xml file from the root of your app and add the following:

<env name="DB_CONNECTION" value="sqlite" />
<env name="DB_DATABASE" value=":memory:" />

We have set up the testing database to be SQLite, so we don't have to use MySQL or any other database you will be using in dev or prod. You can add/implement a setUp and tearDown method to run your migrations, seed your database while your tests run, and reset your migrations when they are done.

public function setUp()
{
    parent::setUp();
    $this->artisan('migrate');
    $this->artisan('db:seed');
}
 
public function tearDown()
{
  $this->artisan('migrate:reset');
}

Let's implement a test to check the get all users route:

public function testCanGetAllUsers()
{
  $this->json('GET', '/users')->seeStatusCode(200);
}

This triggers the index method of the UserController class. If all goes as expected, you should get a 200 OK status code.

Let's run the test:
Screen Shot 2017-10-06 at 11.16.20 AM.png

Attempt to unit test the other endpoints. Test for all possible edge cases. This is the link to the complete code on GitHub.

Hypermedia for Easy API Navigation

A simple way of thinking about this is self documenting your endpoints. A REST client needs no prior knowledge about how to interact with an application or server beyond a generic understanding of Hypermedia.

A typical example is when you create a resource, say, a new user. The client has no prior knowledge about how to find the user resource, therefore the response from the server when the user is created should include information such as:

'_links' => [
  'self' => 'your-app-base-url/user/' . $user->id
 ];

This way, the client is now informed about how to use the just created resource. That is Hypermedia in a nut shell. Read more about HATEOAS.

Recap

  • We went over what REST means and its constraints
  • We built an API using Lumen5.5 to demonstrate how to build an API that is truly RESTful.
  • A truly RESTful API is:
    => resource-based
    => representational
    => stateless
    => implements Hypermedia

I hope you found this helpful!

Discover and read more posts from FLORENCE OKOSUN
get started