© Sanjib Sinha 2019
S. SinhaBeginning Laravel https://doi.org/10.1007/978-1-4842-4991-8_12

12. Working with APIs

Sanjib Sinha1 
(1)
Howrah, West Bengal, India
 

What allows a third party to easily interact with a Laravel application’s data? The answer is an API.

Usually the API is based on JSON and REST or is REST-like. You can easily work with JSON in your Laravel application, which gives Laravel a big advantage over other PHP frameworks. Without an API you cannot interact with any third-party software that is written in a different language and that works on different platforms. So, writing APIs is a common task that Laravel developers do in their jobs.

Another advantage of Laravel is that its resource controllers are already structured around REST verbs and patterns. This makes Laravel developers’ lives much easier.

In this chapter, you will learn to write an API.

What Is REST?

Representational State Transfer (REST) is an architectural style for building APIs.

There are some heated arguments over the definition of REST in the computer world. Please do not get overwhelmed by the definition or get caught in the crossfire. With Laravel, when I talk about REST-like APIs, I generally mean they have a few common characteristics; for example, they are structured around resources, and the APIs can be represented by simple URIs.

The URI http://localhost:8000/articles is a representation of all articles. The URI http:://localhost:8000/article/2 represents the second article. The representation of second article goes on just like normal URI representation.

The stateless condition of APIs makes a big difference. Between requests, there is no persistent session authentication, which means that each request needs to authenticate itself. Finally, the major advantage is it can return JSON, which the server understands. That is the reason why a third party can easily interact with a Laravel application.

The main purpose of building an API is to enable another application to communicate with your application without any issues. The server knows XML and JSON; however, JSON is the most popular choice now. So, when you return your data in JSON, the ease of communication increases.

Creating an API

To create an API, you need controllers and models as usual. But, at the same time, you need to transform your models and model collections into JSON. For that, you want Laravel’s resource classes. They allow you to transform data into JSON.

You can imagine your resource classes as a transformation layer that sits between your Eloquent models and the JSON responses.

Let’s start from scratch. The first step is to create a fresh Laravel project.
//code 12.1
$ composer create-project --prefer-dist laravel/laravel apilara
Let’s say you want to create an Article API . So, you need a model, database table, and controller.
//code 12.2
//creating a controller
$ php artisan make:controller ArticleController --resource
Controller created successfully.
Next, you need an Article model and a database table, as shown here:
//code 12.3
$ php artisan make:model Article -m
The database table should have two fields, title and body, as shown here:
//code 12.4
<?php
use IlluminateSupportFacadesSchema;
use IlluminateDatabaseSchemaBlueprint;
use IlluminateDatabaseMigrationsMigration;
class CreateArticlesTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('articles', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->string('title');
            $table->text('body');
            $table->timestamps();
        });
    }
    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('articles');
    }
}
Before building an API, you should fill the Article table with some fake data with the help of a database seeder. To do this, you need to create ArticlesTableSeeder and ArticleFactory , as shown here:
//code 12.5
$ php artisan make:seeder ArticlesTableSeeder
Seeder created successfully.
//code 12.6
$ php artisan make:factory ArticleFactory
Factory created successfully
Let’s change the code of both ArticlesTableSeeder and ArticleFactory, as shown here:
//code 12.7
//database/seeds/ ArticlesTableSeeder.php
<?php
use IlluminateDatabaseSeeder;
use AppArticle;
class ArticlesTableSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        factory(Article::class, 30)->create();
    }
}
This will create 30 articles. Next, you need to prepare your factory class to fill up the articles table, as shown here:
//code 12.8
// database/factories/ArticleFactory.php
<?php
use FakerGenerator as Faker;
$factory->define(AppArticle::class, function (Faker $faker) {
    return [
        'title' => $faker->text(50),
        'body' => $faker->text(300)
    ];
});

The title property will have a maximum of 50 characters, and for the body you will be content with 300 characters for demonstration purposes.

Let’s fill the database table, as shown here:
//code 12.9
$ php artisan db:seed
Seeding: ArticlesTableSeeder
Database seeding completed successfully.
After the completion of database seeding, you will generate the resource class. To do that, you will use the make:resource Artisan command.
//code 12.10
$ php artisan make:resource Article
Resource created successfully .

These resources are created to transform the individual models. You may want to generate resources that are responsible for transforming collections of models. This allows you to include links and other meta information in your response.

To create a resource collection, either you can use the --collection flag or you can include the word Collection in the resource name. The word Collection will indicate to Laravel that it should create a collection resource. Collection resources extend the IlluminateHttpResourcesJsonResourceCollection class. The relevant artisan command will look like this:
//code 12.13
$ php artisan make:resource Article --collection
$ php artisan make:resource ArticleCollection

Later in this chapter I will discuss collections and show how they work.

So, you have created a resource called Article. You can find it in the app/Http/Resources directory of your application. Resources extend the IlluminateHttpResourcesJsonJsonResource class.
//code 12.14
//app/Http/Resources/Article.php
<?php
namespace AppHttpResources;
use IlluminateHttpResourcesJsonJsonResource;
class Article extends JsonResource
{
    /**
     * Transform the resource into an array.
     *
     * @param  IlluminateHttpRequest  $request
     * @return array
     */
    public function toArray($request)
    {
               return parent::toArray($request);
    }
}

As you can see in the previous code, every resource class defines a toArray method , which returns the array of attributes that should be converted to JSON when sending the response. Laravel takes care of that in the background.

You are not ready yet. You need to register your API routes in the routes/api.php file, like this:
//code 12.15
//routes/api.php
<?php
use IlluminateHttpRequest;
/*
|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------
|
| Here is where you can register API routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| is assigned the "api" middleware group. Enjoy building your API!
|
*/
Route::middleware('auth:api')->get('/user', function (Request $request) {
    return $request->user();
});
//list articles
Route::get('articles', 'ArticleController@index');
//list single article
Route::get('article/{id}', 'ArticleController@show');
//create new article
Route::post('article', 'ArticleController@store');
//update articles
Route::put('article', 'ArticleController@store');
//delete articles
Route::delete('article/{id}', 'ArticleController@destroy');
You see the URI, method, and action parts of your routes. If you want a full list, you can issue this artisan command in your terminal:
$ php artisan route:list
Now you can see the full route list (Figure 12-1).
../images/435350_2_En_12_Chapter/435350_2_En_12_Fig1_HTML.jpg
Figure 12-1

The API route list in your terminal

After starting the local server, you can open Postman to get the response (Figure 12-2). Postman is a tool that can send requests to an API and show you the response. In other words, it is a fancy version of cURL.
../images/435350_2_En_12_Chapter/435350_2_En_12_Fig2_HTML.jpg
Figure 12-2

The JSON response of the Article API in Postman

You can get the same output in your regular web browser also. The URL is http://localhost:8000/api/articles. See Figure 12-3.
../images/435350_2_En_12_Chapter/435350_2_En_12_Fig3_HTML.jpg
Figure 12-3

The same JSON response in reply to a GET request in a normal browser

Let’s take a look at the ArticleController index method, as shown here:
//code 12.16
//app/Http/Controllers/ArticleController.php
<?php
namespace AppHttpControllers;
use IlluminateHttpRequest;
use AppHttpRequests;
use AppArticle;
use AppHttpResourcesArticle as ArticleResource;
class ArticleController extends Controller
{
    /**
     * Display a listing of the resource.
     *
     * @return IlluminateHttpResponse
     */
    public function index()
    {
        //get articles
        $articles = Article::paginate(15);
        //return collection of articles as resource
        return ArticleResource::collection($articles);
    }
    /**
     * Display the specified resource.
     *
     * @param  int  $id
     * @return IlluminateHttpResponse
     */
    public function show($id)
    {
        //get article
        $article = Article::findOrFail($id);
        //returning single article as a resource
        return new ArticleResource($article);
    }
}
For demonstration purposes, I have shown only two methods here: index and show. Since I have used the paginate() method, each page shows you 15 articles. Figure 12-4 shows the second page.
../images/435350_2_En_12_Chapter/435350_2_En_12_Fig4_HTML.jpg
Figure 12-4

The view of page 2 of the JSON response

The URL is http://localhost:8000/api/articles?page=2.

Now you may not want to give your application user every output. You may want to omit created_at and updated_at. Laravel allows you to customize the JSON output. In that case, you can return the selected records like this:
//code 12.17
//app/Http/Resources/Article.php
<?php
namespace AppHttpResources;
use IlluminateHttpResourcesJsonJsonResource;
class Article extends JsonResource
{
    /**
     * Transform the resource into an array.
     *
     * @param  IlluminateHttpRequest  $request
     * @return array
     */
    public function toArray($request)
    {
        return [
            'id' => $this->id,
            'title' => $this->title,
            'body' => $this->body
        ];
    }
}
So, you are returning only id, title, and body and giving a customized look (Figure 12-5).
../images/435350_2_En_12_Chapter/435350_2_En_12_Fig5_HTML.jpg
Figure 12-5

The customized JSON response

You can use the show method to have one article display at a time, as shown in Figure 12-6.
../images/435350_2_En_12_Chapter/435350_2_En_12_Fig6_HTML.jpg
Figure 12-6

JSON response of article number 9

Notice that for the customized version, you access the model properties directly from the $this variable.

How does this work?

This is because a resource class is able to automatically proxy properties and methods, and it accesses the underlying model. Once the resource is defined, it may be returned from a route or controller, as in the ArticleController show method in the example.
//code 12.18
public function show($id)
    {
        //get article
        $article = Article::findOrFail($id);
        //returning single article as a resource
        return new ArticleResource($article);
    }
Here you are returning a single article as a resource. Suppose, in case of the User resource, you return a new UserResource like this:
//code 12.19
use AppUser;
    use AppHttpResourcesUser as UserResource;
    Route::get('/user', function () {
        return new UserResource(User::find(1));
    });

In the customized version, you can add some more elements with the help of the with method in app/Http/Resources/Article.php.

The changed code looks like this:
//code 12.20
//app/Http/Resources/Article.php
<?php
namespace AppHttpResources;
use IlluminateHttpResourcesJsonJsonResource;
class Article extends JsonResource
{
    /**
     * Transform the resource into an array.
     *
     * @param  IlluminateHttpRequest  $request
     * @return array
     */
    public function toArray($request)
    {
        // return parent::toArray($request);
        return [
            'id' => $this->id,
            'title' => $this->title,
            'body' => $this->body
        ];
    }
    public function with($request) {
        return [
            'version' => '1.0.0',
            'author_url' => url('https://sanjib.site')
        ];
    }
}
The JSON response changes to the screen shown in Figure 12-7 in Postman.
../images/435350_2_En_12_Chapter/435350_2_En_12_Fig7_HTML.jpg
Figure 12-7

You have added the version and author URL link, shown in Postman

Next, you will see how API collection works. Let’s first create a UserCollection class .
//code 12.21
$ php artisan make:resource UserCollection
Resource collection created successfully.
I have added a few dummy users to the application. In the resource UserCollection, when you have this line of code:
//code 12.22
//app/Http/Resources/UserCollection.php
public function toArray($request)
    {
        return parent::toArray($request);
    }
you get this JSON response output:
//output of code 12.22
data
0
id     1
name   "sanjib"
email_verified_at    null
created_at   "2019-03-30 00:16:22"
updated_at   "2019-03-30 00:16:22"
1
id     2
name   "ss"
email_verified_at   null
created_at   "2019-03-31 05:02:57"
updated_at   "2019-03-31 05:02:57"
2
id     3
name   "admin"
email_verified_at   null
created_at   "2019-03-31 05:03:11"
updated_at   "2019-03-31 05:03:11"
3
id     4
name   "hagudu"
email_verified_at   null
created_at   "2019-03-31 05:03:24"
updated_at   "2019-03-31 05:03:24"
You can take a look at the result in Figure 12-8.
../images/435350_2_En_12_Chapter/435350_2_En_12_Fig8_HTML.jpg
Figure 12-8

The usual JSON output of UserCollection

Once the resource collection class has been generated, you can easily define any metadata that should be included with the response. To do that, you need to change the code of UserCollection in this way:
//code 12.23
//app/Http/Resources/UserCollection.php
    public function toArray($request)
        {
            return [
                'data' => $this->collection,
                'links' => [
                    'author_url' => 'https://sanjib.site',
                ],
            ];
        }
Now you have added extra metadata, and in the output it has been included at the bottom of your JSON response.
//output of code 12.23
3
id     4
name   "hagudu"
email_verified_at   null
created_at   "2019-03-31 05:03:24"
updated_at   "2019-03-31 05:03:24"
links
author_url   "https://sanjib.site"
//the code is shortened for brevity
In your browser display, it also has been included at the bottom (Figure 12-9).
../images/435350_2_En_12_Chapter/435350_2_En_12_Fig9_HTML.jpg
Figure 12-9

The extra metadata that you have added in the collection

To get all those new UserCollection JSON responses, you can register your route in this way:
//code 12.24
//routes/api.php
use AppUser;
use AppHttpResourcesUserCollection;
Route::get('/users', function () {
      return new UserCollection(User::all());
});

Working with Laravel Passport

You now have authentication in place. As you can see, performing authentication via traditional login forms can easily be done in Laravel.

But API authentication is different. APIs use tokens to authenticate users and do not maintain the session state between requests. Laravel Passport is something that makes developers’ lives much easier because it handles the API authentication.

In fact, it does not take much time to implement API authentication when using Laravel Passport. You will see that in a minute. Moreover, you will learn how the OAuth2 server implementation is done, which Laravel Passport also supports.

If you are not familiar with OAuth2 server implementation, then this will be a small introduction for you. OAuth2 is an open standard for access delegation. Many web sites give you a chance to log in via the GitHub, Google, or Facebook APIs. In such cases, GitHub, Amazon, Google, Microsoft, Twitter, and Facebook give permission to those web sites to access their login information without giving them the passwords. This is known as secured delegated access . These companies authorize the third-party access to their server resources but never share their credentials.

You are going to do the same thing with a local Laravel application and a remote site called https://sanjib.site for demonstration purposes.

To start with, install Passport via the Composer package manager, as shown here:
//code 12.25
$ composer require laravel/passport
  - Installing psr/http-message (1.0.1): Downloading (100%)
  - Installing psr/http-factory (1.0.0): Downloading (100%)
  - Installing zendframework/zend-diactoros (2.1.1): Downloading (100%)
  - Installing symfony/psr-http-message-bridge (v1.2.0): Downloading (100%)
  - Installing phpseclib/phpseclib (2.0.15): Downloading (100%)
  - Installing defuse/php-encryption (v2.2.1): Downloading (100%)
  - Installing lcobucci/jwt (3.2.5): Downloading (100%)
  - Installing league/event (2.2.0): Downloading (100%)
  - Installing league/oauth2-server (7.3.3): Downloading (100%)
  - Installing ralouphie/getallheaders (2.0.5): Downloading (100%)
  - Installing guzzlehttp/psr7 (1.5.2): Downloading (100%)
  - Installing guzzlehttp/promises (v1.3.1): Downloading (100%)
  - Installing guzzlehttp/guzzle (6.3.3): Downloading (100%)
  - Installing firebase/php-jwt (v5.0.0): Downloading (100%)
  - Installing laravel/passport (v7.2.2): Downloading (100%)
Next, migrate because the Passport service provider registers its own database migration directory with the framework.
//code 12.26
$ php artisan migrate
Migrating: 2016_06_01_000001_create_oauth_auth_codes_table
Migrated:  2016_06_01_000001_create_oauth_auth_codes_table
Migrating: 2016_06_01_000002_create_oauth_access_tokens_table
Migrated:  2016_06_01_000002_create_oauth_access_tokens_table
Migrating: 2016_06_01_000003_create_oauth_refresh_tokens_table
Migrated:  2016_06_01_000003_create_oauth_refresh_tokens_table
Migrating: 2016_06_01_000004_create_oauth_clients_table
Migrated:  2016_06_01_000004_create_oauth_clients_table
Migrating: 2016_06_01_000005_create_oauth_personal_access_clients_table
Migrated:  2016_06_01_000005_create_oauth_personal_access_clients_table
Now you need to create the encryption keys needed to generate secure access tokens, as shown here:
//code 12.27
$ php artisan passport:install
Encryption keys generated successfully.
Personal access client created successfully.
Client ID: 1
Client secret: CqTb7j2ABO1qAMM1OHInXodrpTtVkPxFuaR9UZs1
Password grant client created successfully.
Client ID: 2
Client secret: LLxn4BP4Px4q4zJlt4u9JXGe3y4ghUIGQ4TqOv49
As you can see in the previous code, two records, the client ID and the client secret, have been generated (Figure 12-10).
../images/435350_2_En_12_Chapter/435350_2_En_12_Fig10_HTML.jpg
Figure 12-10

oauth_clients in your local database

At this point, you need to check one more thing: whether you have a recent version of Node.js. If not, you need to install it first by running this command:
$ npm install
It will take some time for the Node modules to be installed. You can take a look at your terminal while they are being installed (Figure 12-11).
../images/435350_2_En_12_Chapter/435350_2_En_12_Fig11_HTML.jpg
Figure 12-11

The npm install command is running

Now that you have the Node modules in place, you can tweak some code in your AppUser model. You need to add the LaravelPassportHasApiTokens trait in the User model . This will be required for your API authentication. Replace your User model with this code:
//code 12.28
//app/User.php
<?php
namespace App;
use LaravelPassportHasApiTokens;
use IlluminateNotificationsNotifiable;
use IlluminateContractsAuthMustVerifyEmail;
use IlluminateFoundationAuthUser as Authenticatable;
class User extends Authenticatable
{
    use HasApiTokens, Notifiable;
    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'name', 'email', 'password',
    ];
    /**
     * The attributes that should be hidden for arrays .
     *
     * @var array
     */
    protected $hidden = [
        'password', 'remember_token',
    ];
    /**
     * The attributes that should be cast to native types.
     *
     * @var array
     */
    protected $casts = [
        'email_verified_at' => 'datetime',
    ];
}
Next, you will call the Passport::routes method within the boot method of your AuthServiceProvider . This method will register the routes necessary to issue access tokens and revoke access tokens, clients, and personal access tokens. Replace your old code with this:
//code 12.29
//app/Providers/ AuthServiceProvider.php
<?php
namespace AppProviders;
use LaravelPassportPassport;
use IlluminateSupportFacadesGate;
use IlluminateFoundationSupportProvidersAuthServiceProvider as ServiceProvider;
class AuthServiceProvider extends ServiceProvider
{
    /**
     * The policy mappings for the application.
     *
     * @var array
     */
    protected $policies = [
        // 'AppModel' => 'AppPoliciesModelPolicy',
    ];
    /**
     * Register any authentication / authorization services .
     *
     * @return void
     */
    public function boot()
    {
        $this->registerPolicies();
        Passport::routes();
    }
}
Finally, in your config/auth.php configuration file, you should set the driver option of the api authentication guard to passport. This will instruct your application to use Passport’s TokenGuard when authenticating incoming API requests.
//code 12.30
//config/auth.php
'guards' => [
    'web' => [
        'driver' => 'session',
        'provider' => 'users',
    ],
    'api' => [
        'driver' => 'passport',
        'provider' => 'users',
    ],
],

Now the time has come to use Vue.js for your application API authentication.

Specifically, you are going to create some Vue components. To do that, you have to publish the Passport Vue components, as shown here:
//code 12.31
$ php artisan vendor:publish –tag=passport-components
Next, add these lines inside your resources/js/app.js file:
//code 12.32
//resources/js/app.js
Vue.component(
    'passport-clients',
    require('./components/passport/Clients.vue').default
);
Vue.component(
    'passport-authorized-clients',
    require('./components/passport/AuthorizedClients.vue').default
);
Vue.component(
    'passport-personal-access-tokens',
    require('./components/passport/PersonalAccessTokens.vue').default
);

Now that you have your Vue components in the proper place, you can get the template to create the new OAuth clients.

Since you have a traditional login system already in place for your application, you can get that template in your home.blade.php file .
//code 12.33
//resources/views/home.blade.php
@extends('layouts.app')
@section('content')
<div class="container">
    <div class="row justify-content-center">
        <div class="col-md-8">
            <div class="card">
                <div class="card-header">Dashboard</div>
                <div class="card-body">
                    @if (session('status'))
                        <div class="alert alert-success" role="alert">
                            {{ session('status') }}
                        </div>
                    @endif
                    <passport-clients></passport-clients>
<passport-authorized-clients></passport-authorized-clients>
<passport-personal-access-tokens></passport-personal-access-tokens>
                </div>
            </div>
        </div>
    </div>
</div>
@endsection
Now you are ready to have your users create their own OAuth API clients. Once a user is signed in, they will be greeted by the new look shown in Figure 12-12.
../images/435350_2_En_12_Chapter/435350_2_En_12_Fig12_HTML.jpg
Figure 12-12

The user has not created any client yet

Now the user is going to create the first OAuth client (Figure 12-13).
../images/435350_2_En_12_Chapter/435350_2_En_12_Fig13_HTML.jpg
Figure 12-13

The user is going to create the new OAuth client

After the first OAuth client has been created, it will immediately be reflected on the page (Figure 12-14).
../images/435350_2_En_12_Chapter/435350_2_En_12_Fig14_HTML.jpg
Figure 12-14

The first OAuth client has been created

Now you have come to the final stage, which is API authentication.

API Authentication

You have seen how a user has added an OAuth client on their own home page. But a user with administrative privileges can add many clients for other users. Suppose you have a user who has an ID of 2.

In that case, the administrator can issue the following commands in the terminal to create the OAuth client for that user:
//code 12.34
$ php artisan passport:client
 Which user ID should the client be assigned to?:
 > 2
 What should we name the client?:
 > sanjib
 Where should we redirect the request after authorization? [http://localhost/auth/callback]:
 >
New client created successfully.
Client ID: 4
Client secret: E02repG4eeeklfpaeX8i6YdtDi2tYQS6aYuKIO8I
Immediately after this addition, the user with ID 2 will see the OAuth client on their home.blade.php page (Figure 12-15).
../images/435350_2_En_12_Chapter/435350_2_En_12_Fig15_HTML.jpg
Figure 12-15

A new OAuth client has been added by the administrator

Now what happens if a remote site (here https://sanjib.site ) wants your application to authorize user 5?

You will be able to authorize it without giving your login credentials.

To do that, in the routes/web.php file of https://sanjib.site , there is this code:
//code 12.35
Route::get('/redirect', function () {
    $query = http_build_query([
        'client_id' => '5',
        'redirect_uri' => 'https://sanjib.site/callback',
        'response_type' => 'code',
        'scope' => ",
    ]);
    return redirect('http://localhost:8000/oauth/authorize?'.$query);
});
This will redirect the page to the login screen of http://localhost:8000/login, as shown in Figure 12-16.
../images/435350_2_En_12_Chapter/435350_2_En_12_Fig16_HTML.jpg
Figure 12-16

Redirected to the login screen of http://localhost:8000/login

Remember, the remote site ( https://sanjib.site ) asks for your permission to allow the user with ID 5. This happens because this route has already been registered in the web.php file of https://sanjib.site .

For that reason, only the fifth user can view this login page. If some other users want to log in, an error will be thrown.

However, once the fifth user logs in, they will be greeted with the page in Figure 12-17.
../images/435350_2_En_12_Chapter/435350_2_En_12_Fig17_HTML.jpg
Figure 12-17

sanjib.site is requesting permission to access the account of the user with an ID of 5

Now, if you look at the URL in the browser, you will see something similar to this:
//code 12.36
http://localhost:8000/oauth/authorize?client_id=5&redirect_uri=https%3A%2F%2Fsanjib.site%2Fcallback&response_type=code&scope=

Laravel APIs, Passport, and API authentication features are great additions to easily manage the difficulties of authenticating the OAuth client.

I hope you have an idea of how you could use these great features in your application.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.147.54.242