Dingo API Definitive Guide, Part 1: Quick Start

APIs are everywhere, from Web sites to mobile applications, to IoT devices and autonomous cars, and they are changing the way how we develop. Today there so in no need to create one code base for user panel and the other code base for backend of mobile application when you can create single API which will be used by many devices. 

Laravel is my framework of choice and when it comes to Laravel ecosystem, Dingo API is by far the most powerful API package. I used Dingo in over 50 different projects and development speed is absolutely the biggest plus. It is still in development stage but that does not affect the quality of the package, it has over 1000 commits and 50+ contributors, just remember Composer also was not stable till April 2016, but entire world was using it. 

Dingo API posses built-in authentication, error handling, responses, pagination, rate limiting, transformers and much more. In this first part I will drive you through Dingo API installation and configuration. 

You can find entire code used in this tutorial on Github.

Installation

I am starting from clean Laravel 5.2 installation. I will pull Dingo package via composer but before I can do that I need to set minimum stability to development in composer.json, like this:

"minimum-stability": "dev"

Now I will pull the package:

composer require dingo/api:[email protected]

Configuration

When composer is done with installation I will go to config/app.php and add new service provider to providers array.

Dingo\Api\Provider\LaravelServiceProvider::class

Dingo requires few env variables to be configured, without further details I will add following variables to .env:

API_STANDARDS_TREE=vnd
API_SUBTYPE=dingo-api-guide
API_DOMAIN=dingo-api-definitive-guide.dev
API_VERSION=v1
API_NAME="Dingo API Definitive Guide"

That would be it! 

Creating Test Endpoint

Now I will check is everything working fine by creating test endpoint with Dingo API.

Dingo API posses it's own router, this was done to prevent any complications with other project routes. So first I'll need to create instance of Dingo router in routes.php.

$api = app('Dingo\Api\Routing\Router');

 It expects to receive API version in each request, this is accomplished by grouping certain routes with same version. For now we are using version 1:

$api->version('v1', function ($api) {

    $api->get('test', function () {
        return 'It is ok';
    });

});

If you visit this new route (http://dingo-api-definitive-guide.dev/test) with browser or issue Postman GET request you should see It is ok.

This means your installation went well and you can start using Dingo API.

Seeding the Database

I will be using Laravel's default users table migration and create seeder for that table. The main reason why I am doing this, is to show you how you can use Fractal Transformers and built-in response helper to craft API responses.

My new UserSeeder looks like this:

<?php

use Illuminate\Database\Seeder;

use Illuminate\Support\Facades\DB;
use App\Models\User;

class UserSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        DB::table('users')->delete();
        
        User::create([
            'name' => 'John Doe',
            'email' => [email protected]',
            'password' => bcrypt('password')
        ]);

        User::create([
            'name' => 'Jane Doe',
            'email' => [email protected]',
            'password' => bcrypt('password')
        ]);

        User::create([
            'name' => 'Jason Bourne',
            'email' => [email protected]',
            'password' => bcrypt('jasonB')
        ]);
    }
}

You will notice from use statement that I moved User model from app directory to new app/Models directory.

Now I will migrate and seed the database with: 

php artisan migrate --seed

Creating Transformer Class

 As their name reveals, transformer classes are used to modify data representations. In this scenario we are using them to change model data that we are returning to API user.

For example I want to return only name and email with new piece of data named added, which represents the date when user was created in format 'Y-m-d'.

First I will create separate directory for all transformer classes in app\Transformers. Inside new directory I'll create new class UserTransformer with following content:

<?php

namespace App\Transformers;

use League\Fractal\TransformerAbstract;
use App\Models\User;

class UserTransformer extends TransformerAbstract
{
    public function transform(User $user)
    {
        return [
            'name' => $user->name,
            'email' => $user->email,
            'added' => date('Y-m-d', strtotime($user->created_at))
        ];
    }
}

Transformer classes have only one required method, transform. It is expecting model instance which needs transformation and it returns array of data.

Real magic starts when you try to use built-in Dingo responses with transformer classes. With one implementation of transformer class you can return single object, collection or even pagination.

Responses

For better code organization I will move logic from routes.php file and insert it into new UserController. I will insert logic for testing transformers in getUsers method. 

<?php

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use App\Transformers\UserTransformer;
use Dingo\Api\Routing\Helpers;
use App\Models\User;

class UserController extends Controller
{
    use Helpers;
    
    public function getUsers()
    {
        
        $users = User::all();

        return $this->response->collection($users, new UserTransformer);

    }
}

From code above, you can see that I am using Helpers trait from Dingo package. That trait posses Dingo's response logic through response property. In getUsers method you can see that I am taking all users and passing them to collection function of response property. First parameter is Laravel collection and second parameter is instance of wanted transformer class.

Cause I moved logic to new controller, I need to update routes file:

$api = app('Dingo\Api\Routing\Router');

$api->version('v1', [], function ($api) {
    
    $api->get('users', 'App\Http\Controllers\Api\[email protected]');
});

If you visit this new /users route via Postman or browser you should see:

{
  "data": [
    {
      "name": "John Doe",
      "email": "[email protected]",
      "added": "2016-07-04"
    },
    {
      "name": "Jane Doe",
      "email": "[email protected]",
      "added": "2016-07-04"
    },
    {
      "name": "Jason Bourne",
      "email": "[email protected]",
      "added": "2016-07-04"
    }
  ]
}

From response you see that Dingo API is using new transformer class for entire collection.

To see full power of responses I will switch response from collection to pagination. For this I need to modify UserController

 public function getUsers()
    {

        $users = User::paginate(2);

        return $this->response->paginator($users, new UserTransformer);

    }

I modified only to pieces of code, instead of selecting all records in users table I am retrieving 2 by 2 via paginate method. And in response property I am now using paginator function, everything else is the same.

Response is pretty cool:

{
  "data": [
    {
      "name": "John Doe",
      "email": "[email protected]",
      "added": "2016-07-04"
    },
    {
      "name": "Jane Doe",
      "email": "[email protected]",
      "added": "2016-07-04"
    }
  ],
  "meta": {
    "pagination": {
      "total": 3,
      "count": 2,
      "per_page": 2,
      "current_page": 1,
      "total_pages": 2,
      "links": {
        "next": "http://dingo-api-definitive-guide.dev/users?page=2"
      }
    }
  }
}

We did not code this meta key of the response, or anything related to pagination, but it works.

If you visit second page from next key you should see:

{
  "data": [
    {
      "name": "Jason Bourne",
      "email": "[email protected]",
      "added": "2016-07-04"
    }
  ],
  "meta": {
    "pagination": {
      "total": 3,
      "count": 1,
      "per_page": 2,
      "current_page": 2,
      "total_pages": 2,
      "links": {
        "previous": "http://dingo-api-definitive-guide.dev/users?page=1"
      }
    }
  }
}

Everything works, and Dingo API is using the best practices for constructing RESTful responses.

That's all for this quick start, feel free to brag about newly acquired API development skills :-)

Trending

Newsletter

Subscribe to our newsletter for good news, sent out every month.

Tags