How to build a Laravel REST API with Test-Driven Development

How to build a Laravel REST API with Test-Driven Development

In this article, we’ll be going on a Laravel journey driven by tests. We’ll create a Laravel REST API complete with authentication and CRUD functionality without opening Postman or a browser. 😲

In this article, we’ll be going on a Laravel journey driven by tests. We’ll create a Laravel REST API complete with authentication and CRUD functionality without opening Postman or a browser. 😲

There is a famous quote by James Grenning, one of the pioneers in TDD and Agile development methodologies:

If you’re not doing test-driven development, you’re doing debug-later development - James Grenning> If you’re not doing test-driven development, you’re doing debug-later development - James Grenning## Setting up the project

Start by creating a new Laravel project with composer create-project --prefer-dist laravel/laravel tdd-journey.

Next, we need to run the authentication scaffolder that we would use, go ahead and run php artisan make:auth then php artisan migrate.

We will not actually be using the routes and views generated. For this project, we would be using jwt-auth. So go ahead and set it up in your application.

If you’re not doing test-driven development, you’re doing debug-later development - James Grenning
Finally, you can delete ExampleTest in both the tests/Unit and tests/Feature folders so that it doesn’t interfere with our test results and we’re good to go.

Writing the code
  1. Begin by setting your auth configuration to use the JWT driver as default:
<?php 
// config/auth.php file
'defaults' => [
    'guard' => 'api',
    'passwords' => 'users',
],
'guards' => [
    ...
    'api' => [
        'driver' => 'jwt',
        'provider' => 'users',
    ],
],

Then add the following to your routes/api.php file:

<?php
Route::group(['middleware' => 'api', 'prefix' => 'auth'], function () {
    Route::post('authenticate', '[email protected]')->name('api.authenticate');
    Route::post('register', '[email protected]')->name('api.register');
});

  1. Now that we have our driver set up, set up your user model in the same way:
<?php
...
class User extends Authenticatable implements JWTSubject
{
    ...
     //Get the identifier that will be stored in the subject claim of the JWT.
    public function getJWTIdentifier()
    {
        return $this->getKey();
    }
    // Return a key value array, containing any custom claims to be           added to the JWT.
    public function getJWTCustomClaims()
    {
        return [];
    }
}

What we did was that we just implemented the JWTSubject and added the required methods.

  1. Next, we need to add our authentication methods in the controller.

Run php artisan make:controller AuthController and add the following methods:

<?php
...
class AuthController extends Controller
{
    
    public function authenticate(Request $request){
        //Validate fields
        $this->validate($request,['email' => 'required|email','password'=> 'required']);
        //Attempt validation
        $credentials = $request->only(['email','password']);
        if (! $token = auth()->attempt($credentials)) {
            return response()->json(['error' => 'Incorrect credentials'], 401);
        }
        return response()->json(compact('token'));
    }
    public function register(Request $request){
        //Validate fields
        $this->validate($request,[
            'email' => 'required|email|max:255|unique:users',
            'name' => 'required|max:255',
            'password' => 'required|min:8|confirmed',
        ]);
        //Create user, generate token and return
        $user =  User::create([
            'name' => $request->input('name'),
            'email' => $request->input('email'),
            'password' => Hash::make($request->input('password')),
        ]);
        $token = JWTAuth::fromUser($user);
        return response()->json(compact('token'));
    }
}

This step is pretty straight forward, all we do is add the authenticate and register methods to our controller. In the authenticate method, we validate the input, attempt a login and return the token if successful. In the register method, we validate the input, create a new user with the input and generate a token for the user based on that.

  1. Next, onto the good part. Testing what we just wrote. Generate the test classes using php artisan make:test AuthTest. In the new tests/Feature/AuthTest add these methods:
<?php 
/**
 * @test 
 * Test registration
 */
public function testRegister(){
    //User's data
    $data = [
        'email' => '[email protected]',
        'name' => 'Test',
        'password' => 'secret1234',
        'password_confirmation' => 'secret1234',
    ];
    //Send post request
    $response = $this->json('POST',route('api.register'),$data);
    //Assert it was successful
    $response->assertStatus(200);
    //Assert we received a token
    $this->assertArrayHasKey('token',$response->json());
    //Delete data
    User::where('email','[email protected]')->delete();
}
/**
 * @test
 * Test login
 */
public function testLogin()
{
    //Create user
    User::create([
        'name' => 'test',
        'email'=>'[email protected]',
        'password' => bcrypt('secret1234')
    ]);
    //attempt login
    $response = $this->json('POST',route('api.authenticate'),[
        'email' => '[email protected]',
        'password' => 'secret1234',
    ]);
    //Assert it was successful and a token was received
    $response->assertStatus(200);
    $this->assertArrayHasKey('token',$response->json());
    //Delete the user
    User::where('email','[email protected]')->delete();
}

The comments in the code above pretty much describes the code. One thing you should note is how we create and delete the user in each test. The whole point of tests are that they should be independent of each other and the database state ideally.

Now run $vendor/bin/phpunit or $ phpunit if you have it globally installed. Running that should give you successful assertions. If that was not the case, you can look through the logs, fix and retest. This is the beautiful cycle of TDD.

  1. Now that we have our authentication working, let’s add the item for the CRUD. For this tutorial, we’re going to use food recipes as our CRUD items, because, why not?

Start by creating our migration php artisan make:migration createrecipestable and add the following:

<?php 
...
public function up()
{
    Schema::create('recipes', function (Blueprint $table) {
        $table->increments('id');
        $table->string('title');
        $table->text('procedure')->nullable();
        $table->tinyInteger('publisher_id')->nullable();
        $table->timestamps();
    });
}
public function down()
{
    Schema::dropIfExists('recipes');
}

Then run the migration. Now add the model using php artisan make:model Recipe and add this to our model.

<?php 
...
protected $fillable = ['title','procedure'];
/**
 * The owner of this delicious recipe
 * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
 */
public function publisher(){
    return $this->belongsTo(User::class);
}

Then add this method to the user model.

<?php
...
  /**
 * Get all recipes
 * @return \Illuminate\Database\Eloquent\Relations\HasMany
 */
public function recipes(){
    return $this->hasMany(Recipe::class);
}

  1. Now we need endpoints for managing our recipes. First, we’ll create the controller php artisan make:controller RecipeController. Next, edit the routes/api.php file and add the create endpoint.
<?php 
...
  Route::group(['middleware' => ['api','auth'],'prefix' => 'recipe'],function (){
    Route::post('create','[email protected]')->name('recipe.create');
});

In the controller, add the create method as well

<?php 
...
  public function create(Request $request){
    //Validate
    $this->validate($request,['title' => 'required','procedure' => 'required|min:8']);
    //Create recipe and attach to user
    $user = Auth::user();
    $recipe = Recipe::create($request->only(['title','procedure']));
    $user->recipes()->save($recipe);
    //Return json of recipe
    return $recipe->toJson();
}

Generate the feature test with php artisan make:test RecipeTest and edit the contents as under:

<?php 
...
class RecipeTest extends TestCase
{
    use RefreshDatabase;
    ...
    //Create user and authenticate the user
    protected function authenticate(){
        $user = User::create([
            'name' => 'test',
            'email' => '[email protected]',
            'password' => Hash::make('secret1234'),
        ]);
        $token = JWTAuth::fromUser($user);
        return $token;
    }
  
    public function testCreate()
    {
        //Get token
        $token = $this->authenticate();
        $response = $this->withHeaders([
            'Authorization' => 'Bearer '. $token,
        ])->json('POST',route('recipe.create'),[
            'title' => 'Jollof Rice',
            'procedure' => 'Parboil rice, get pepper and mix, and some spice and serve!'
        ]);
        $response->assertStatus(200);
    }
}

The code is quite self-explanatory. All we do is create a method that handles the registering of a user and token generation, then we use that token in the testCreate() method. Note the use of the RefreshDatabase trait, the trait is Laravel’s convenient way of resetting your database after each test, which is perfect for our nifty little project.

OK, so for now, all we want to assert is the status of the response, go ahead and run $ vendor/bin/phpunit.

If all goes well, you should receive an error. 😆

There was 1 failure:
1) Tests\Feature\RecipeTest::testCreate
Expected status code 200 but received 500.
Failed asserting that false is true.
/home/user/sites/tdd-journey/vendor/laravel/framework/src/Illuminate/Foundation/Testing/TestResponse.php:133
/home/user/sites/tdd-journey/tests/Feature/RecipeTest.php:49
FAILURES!
Tests: 3, Assertions: 5, Failures: 1.

Looking at the log files, we can see the culprit is the publisher and recipesrelationship in the Recipe and User classes. Laravel tries to find a user_idcolumn in the table and use that as the foreign key, but in our migration we set publisher_id as the foreign key. Now, adjust the lines as under:

//Recipe file
public function publisher(){
    return $this->belongsTo(User::class,'publisher_id');
}
//User file
public function recipes(){
    return $this->hasMany(Recipe::class,'publisher_id');
}

And then re-run the test. If all goes well we get all green tests! 👍

...                                                                 3 / 3 (100%)
...
OK (3 tests, 5 assertions)

Now we still need to test the creation of the recipe. To do that we can assert the recipes count of the user. Update your testCreate method as under:

<?php
...
//Get token
$token = $this->authenticate();
$response = $this->withHeaders([
    'Authorization' => 'Bearer '. $token,
])->json('POST',route('recipe.create'),[
    'title' => 'Jollof Rice',
    'procedure' => 'Parboil rice, get pepper and mix, and some spice and serve!'
]);
$response->assertStatus(200);
//Get count and assert
$count = User::where('email','[email protected]')->first()->recipes()->count();
$this->assertEquals(1,$count);

We can now go ahead and fill the rest of our methods. Time for some changes. First, our routes/api.php

<?php
...
Route::group(['middleware' => ['api','auth'],'prefix' => 'recipe'],function (){
    Route::post('create','[email protected]')->name('recipe.create');
    Route::get('all','[email protected]')->name('recipe.all');
    Route::post('update/{recipe}','[email protected]')->name('recipe.update');
    Route::get('show/{recipe}','[email protected]')->name('recipe.show');
    Route::post('delete/{recipe}','[email protected]')->name('recipe.delete');
});

Next, we add the methods to the controller. Update your RecipeControllerclass this way.

<?php 
....
//Create recipe
public function create(Request $request){
    //Validate
    $this->validate($request,['title' => 'required','procedure' => 'required|min:8']);
    //Create recipe and attach to user
    $user = Auth::user();
    $recipe = Recipe::create($request->only(['title','procedure']));
    $user->recipes()->save($recipe);
    //Return json of recipe
    return $recipe->toJson();
}
//Get all recipes
public function all(){
    return Auth::user()->recipes;
}
//Update a recipe
public function update(Request $request, Recipe $recipe){
    //Check is user is the owner of the recipe
    if($recipe->publisher_id != Auth::id()){
        abort(404);
        return;
    }
    //Update and return
    $recipe->update($request->only('title','procedure'));
    return $recipe->toJson();
}
//Show a single recipe's details
public function show(Recipe $recipe){
    if($recipe->publisher_id != Auth::id()){
        abort(404);
        return;
    }
    return $recipe->toJson();
}
//Delete a recipe
public function delete(Recipe $recipe){
    if($recipe->publisher_id != Auth::id()){
        abort(404);
        return;
    }
    $recipe->delete();
}

The code and comments already explain the logic to a good degree.

Lastly our test/Feature/RecipeTest

<?php
...
  use RefreshDatabase;
protected $user;
//Create a user and authenticate him
protected function authenticate(){
    $user = User::create([
        'name' => 'test',
        'email' => '[email protected]',
        'password' => Hash::make('secret1234'),
    ]);
    $this->user = $user;
    $token = JWTAuth::fromUser($user);
    return $token;
}
//Test the create route
public function testCreate()
{
    //Get token
    $token = $this->authenticate();
    $response = $this->withHeaders([
        'Authorization' => 'Bearer '. $token,
    ])->json('POST',route('recipe.create'),[
        'title' => 'Jollof Rice',
        'procedure' => 'Parboil rice, get pepper and mix, and some spice and serve!'
    ]);
    $response->assertStatus(200);
    //Get count and assert
    $count = $this->user->recipes()->count();
    $this->assertEquals(1,$count);
}
//Test the display all routes
public function testAll(){
    //Authenticate and attach recipe to user
    $token = $this->authenticate();
    $recipe = Recipe::create([
        'title' => 'Jollof Rice',
        'procedure' => 'Parboil rice, get pepper and mix, and some spice and serve!'
    ]);
    $this->user->recipes()->save($recipe);
    //call route and assert response
    $response = $this->withHeaders([
        'Authorization' => 'Bearer '. $token,
    ])->json('GET',route('recipe.all'));
    $response->assertStatus(200);
    //Assert the count is 1 and the title of the first item correlates
    $this->assertEquals(1,count($response->json()));
    $this->assertEquals('Jollof Rice',$response->json()[0]['title']);
}
//Test the update route
public function testUpdate(){
    $token = $this->authenticate();
    $recipe = Recipe::create([
        'title' => 'Jollof Rice',
        'procedure' => 'Parboil rice, get pepper and mix, and some spice and serve!'
    ]);
    $this->user->recipes()->save($recipe);
    //call route and assert response
    $response = $this->withHeaders([
        'Authorization' => 'Bearer '. $token,
    ])->json('POST',route('recipe.update',['recipe' => $recipe->id]),[
        'title' => 'Rice',
    ]);
    $response->assertStatus(200);
    //Assert title is the new title
    $this->assertEquals('Rice',$this->user->recipes()->first()->title);
}
//Test the single show route
public function testShow(){
    $token = $this->authenticate();
    $recipe = Recipe::create([
        'title' => 'Jollof Rice',
        'procedure' => 'Parboil rice, get pepper and mix, and some spice and serve!'
    ]);
    $this->user->recipes()->save($recipe);
    $response = $this->withHeaders([
        'Authorization' => 'Bearer '. $token,
    ])->json('GET',route('recipe.show',['recipe' => $recipe->id]));
    $response->assertStatus(200);
    //Assert title is correct
    $this->assertEquals('Jollof Rice',$response->json()['title']);
}
//Test the delete route
public function testDelete(){
    $token = $this->authenticate();
    $recipe = Recipe::create([
        'title' => 'Jollof Rice',
        'procedure' => 'Parboil rice, get pepper and mix, and some spice and serve!'
    ]);
    $this->user->recipes()->save($recipe);
    $response = $this->withHeaders([
        'Authorization' => 'Bearer '. $token,
    ])->json('POST',route('recipe.delete',['recipe' => $recipe->id]));
    $response->assertStatus(200);
    //Assert there are no recipes
    $this->assertEquals(0,$this->user->recipes()->count());
}

Other than the additional test, the only other difference was adding a class-wide user file. That way, the authenticate method not only generates a token, but it sets the user file for subsequent operations.

Now run $ vendor/bin/phpunit and you should have all green tests if done correctly.

Conclusion

Hopefully, this gave you an insight into how TDD works in Laravel. It is definitely a much wider concept than this, one that is not bound to a specific method.

Though this method of development may seem longer than the usual debug later* *procedure, it’s perfect for catching errors early on in your code. Though there are cases where a non-TDD approach is more useful, it’s still a solid skill and habit to get used to.

The entire code for this walkthrough is available on Github here. Feel free to play around with it.

*Originally published by ****Kofo Okesola ***at freecodecamp.org

=========================

Thanks for reading :heart: If you liked this post, share it with all of your programming buddies! Follow me on Facebook | Twitter

Tutorial Laravel 6 with Docker and Docker-Compose

Getting Started with Laravel 6 Model Events

Laravel 6 tutorial – Build Your First CRUD App with Example

How to use form validation in Laravel 6

Angular 9 Tutorial: Learn to Build a CRUD Angular App Quickly

What's new in Bootstrap 5 and when Bootstrap 5 release date?

What’s new in HTML6

How to Build Progressive Web Apps (PWA) using Angular 9

What is new features in Javascript ES2020 ECMAScript 2020

Integrate PHP Laravel 5.8 APIs in Angular 7

Integrate PHP Laravel 5.8 APIs in Angular 7

For PHP Laravel APIs to integrate in Angular 7, we have to write APIs in api.php file in Laravel which is in the routes folder in Laravel project structure. For the sake of this article, we are using example of User API.

we commonly use PHP Laravel 5 for database integration and Angular for frontend single page applications. So it makes sense to write about integrating PHP Laravel APIs with Angular frontend.

1. Develop Laravel API

Lets setup the database first. We follow proper Laravel structure through commands and code.

1.1 Create Laravel API migration

For PHP Laravel APIs to integrate in Angular 7, we have to write APIs in api.php file in Laravel which is in the routes folder in Laravel project structure. For the sake of this article, we are using example of User API. For User API to fetch and save data, you have to first setup database table. For that you have to write the migration for the table the command to create migration is written below.

php artisan make:migration create_user_table

After creating a migration, you have to make a model for the table that you have created. Which can be made by command written below.

1.2 Laravel model command

php artisan make:model User

short hand for creating migration and model both you will use this command

php artisan make:model User -m

After using this command the migration and model is made.

1.3 Laravel migration code example

Now you can write table in the migration. Below is the example to write table in migration.

public function up()
 {
 Schema::create('users', function (Blueprint $table) {
 $table-&gt;increments('id');
 $table-&gt;string('email')-&gt;unique();
 $table-&gt;string('first_name');
 $table-&gt;string('last_name');
 $table-&gt;string('password');
 $table-&gt;enum('account_status',['active','pending','rejected'])-&gt;default('pending');
 $table-&gt;enum('gender',['male','female']);
 $table-&gt;timestamps();
 });
 }

You can see the more about how to write table in the migration go to this link:

https://laravel.com/docs/5.7/migrations. After writing the table you have to run this command to migrate the table into the database

php artisan migrate

After migration, you have to write a method into the model that was created to get, update, or add data into the table. For an API to run you have to make,

Route::post('allUsers','[email protected]');

1. 4 Laravel controller command

php artisan make:controller UserController

1.4 Laravel controller code example

The controller function will look like this.

public function getAllUsers(){
 $count = User::All()-&gt;count();
 if($count &gt; 0){
 $users = User::role(['user','admin','dealer'])-&gt;get();
 return response()-&gt;json($users);
 }else{
 return response()-&gt;json("Users Not Found");
 }
 }

You can also make resource and collection to return the response. You can go this link and check the eloquent resources and collection https://laravel.com/docs/5.7/eloquent-resources.

So in this api the api is using UserController Method getllAllUsers to get the users from the database. After that you have to serve the laravel project to use these apis into angular project.

2. Integrate Laravel API in Angular

First setup your angular project using this command. Using Angular Cli.

ng new project_name

After that make your first component. For making component use this command.

ng generate component Users
// Or short hand:
ng g c Users

Four files will be generated after running that command. After that design your page in the html file

and in the ts file your code goes there. After that you have to make service to integrate the api calls that has been written in the Laravel so to create a service run this command.

ng generate service User
// Or short hand:
ng g s User

After running this command 2 file will be generated you will write the code in the ts file name users.service.ts there you will first import the HttpClient first. Then go to the environment folder and write the the api served url of the laravel project for local the url will look like this.

http://127.0.0.1:8000

The Laravel project will always serve on the 8000 port.

After importing the HttpClient then import the environment.ts file in the users service and get the api url from there. For importing just import the HttpClient into the constructor of the service.ts file.

After importing our service constructor will look like this.

 apiUrl: any;
constructor(private http: HttpClinet){
this.apiUrl = environment.apiUrl;
}

After that write a function to to call the API of the Laravel and we will use the HttpClient in the function an example is written below.

getAllUsers() {
 return this.http.get(this.apiUrl + 'allUsers/')
 .pipe(map((response: any) =&gt; {
 return response.json();
 }));
 }

This function is returning the data of all the users and parse then into the JSON response and return from where this function will get called.

After creating this function to get the data from the Laravel API now we will use this function into the the component that we have created first. To use the users service function import the users.service.ts file into the constructor of the users component. So this is the example to use the this service into the component.

The constructor will look like this after importing

constructor(private usersService: UsersService){
}

Here you can change the variable name for the user Service. You can set the variable name as you like. There is an ngOnInit() function in every component this is the first function to get called when ever the project gets served or this component is called. To get all the users from the service function that is calling Laravel API call to get all users the code will look like this.

allusers: any;
getUsers(){
this.usersService. getAllUsers()
.subscribe({
response =&gt; {
this.allusers=response;
},
 });
}

Here we have made first a global variable that is storing the response from the call into itself so that the response can be used the users.component.html file and anywhere in the users.component.ts file.

The getUsers function is calling the service function gellAllUsers to get the user and save it into the local variable. Now you can use this variable in whatever the way you like into your html page to show it on the webpage.

In Angular, you can also make the model to map the response also so for that you need to make a file names users.model.ts there is no specific command for that. So the model is made for the easiness to use the response of the API. Example is given below.

export class User{
 id: number;
 first_name: string;
 last_name: string;
 email: string;
 gender: string;
 status: string;
}

For mapping the response on to the model we will use Observables. Observable provide the support for passing the messages between creators of the observables and the subscribers in our application. Here the subscriber will be our getUsers function in the users.component.ts file and the Observable will be defined in the users.service.ts.

For using observers we will import the rxjs Observable into the users.service.ts file. Then after that the Observable will be used like this as shown in example below.

getUsers(): Observable&lt;{data: Users[]}&gt;{
 const headers = new HttpHeaders({'Content-Type': 'application/json'});
 return this.http.get&lt;{status_code: number, data: Users[], message: string}&gt;(this.apiUrl +
'allUsers', {headers: headers});
 }

Here the observable is parsing the response into the user model which we have created above as we are getting all the users from the API response so we are use Users[] array. And to get its parsed over here you have to format the response also in the Laravel API function. After this we also need to change the users.component.ts file so it will look like this.

 

allusers: Users[];

First we need to make your allUsers variable type to Users[] as the model and as we have done it in the service file the the gellUsers function will able to map response now.

getUsers(){
this.usersService. getAllUsers()
.subscribe({
response =&gt; {
this.allusers=response;
},
 });
}

Now it has made it easy for you to get the variable easily that are in the users model if you want to see the firstname of all the users you can easily do this

this.allusers.foreach(element =&gt;{
console.log('First Name',element.firstname);
})

as firstname is the variable that is in the model of Users.

If you follow this detailed tutorial, you can learn to write the APIs in Laravel and to integrate them in Angular project.

Thanks for reading. If you liked this post, share it with all of your programming buddies!

This post was originally published here

Laravel API Response - Simple Laravel API response wrapper

Laravel API Response - Simple Laravel API response wrapper

Laravel API Response is a good package that we can use to create API responses easily.

Simple and ready to use API response wrapper for Laravel - Simple Laravel API response wrapper.

Installation
  1. Install package through composer: $ composer require obiefy/api-response
  2. publish config file : php artisan vendor:publish --tag=api-response
Basic usage

Create and return JSON response:

use Obiefy\API\Facades\API;
...
public function index()
{
    $users = User::all();
return API::response(200,'users list', $users);

}

Or you can use helper function:

use Obiefy\API\Facades\API;
...
public function index()
{
$users = User::all();

return api()-&gt;response(200, 'users list', $users);

}

Advanced usage

1. General example

use Obiefy\API\Facades\API;
...
public function index()
{
$users = User::all();

return API::response(200, 'users list', $users);

}

result:

{
"STATUS": 200,
"MESSAGE": "users list",
"DATA": [
{"name": "user name"}
]
}

2. Success response

return api()->ok('Success message`, [
'name' => 'Obay Hamed'
]);

result:

{
"STATUS": 200,
"MESSAGE": "Success message",
"DATA": {"name": "Obay Hamed"}
}

you can also return success message with out passing parametters

return api()->ok();

in this case response message will be the default message from config file config('api.messages.success') the same thing for api()->notFound() and api()->validation().

Source Code: https://github.com/obiefy/api-response

Thanks for reading

If you liked this post, share it with all of your programming buddies!

Follow me on Facebook | Twitter

Further reading

Laravel 5.8 Tutorial for Beginners

Tutorial Laravel 6 with Docker and Docker-Compose

How to Laravel 6 Database Migrations - Add Admin Role to Users

Laravel 6 Release New Features and Upgrade

Laravel 6 CRUD Application Tutorial


How to Creat an API in Laravel with Passport

How to Creat an API in Laravel with Passport

You will be learning how to build an API in Laravel Passport and how to generate a token for a user. This token is used to authenticate the user.

In this post, I will be creating an API with Laravel Passport. Laravel Passport provides an easy way to perform API authentication. You will be learning how to build an API in Laravel Passport and how to generate a token for a user. This token is used to authenticate the user.

Installation

Let’s start by installing Laravel Passport via Composer.

composer require laravel/passport

Laravel Passport will create its own tables to store client and access tokens. So we should migrate the database after installing Passport.

php artisan migrate

Next, we will have to create the encryption keys that are needed to generate secure access tokens. We can do this by executing the following command:

php artisan passport:install

Add the HasApiTokens trait to the User model.

<?php
namespace App;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Laravel\Passport\HasApiTokens;
class User extends Authenticatable
{
    use HasApiTokens, Notifiable;
    protected $fillable = [
        'name', 'email', 'password',
    ];
    protected $hidden = [
        'password', 'remember_token',
    ];
    protected $casts = [
        'email_verified_at' => 'datetime',
    ];
}

In the AuthServiceProvider we should call Passport::routes() within the boot method. This will register routes that are needed to issue access tokens and revoke tokens.

<?php
namespace App\Providers;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
use Laravel\Passport\Passport;
class AuthServiceProvider extends ServiceProvider
{
    protected $policies = [];
    public function boot()
    {
        $this->registerPolicies();
        Passport::routes();
    }
}

There is one more thing that we need to do before we can start building the API. In the config/auth.php change the guards.api.driver to passport.

Defining a route

For now, we will be using a very simple example in the routes/api.php.

<?php

use App\Book;
use App\Http\Resources\Book as BookResource;
use Illuminate\Http\Request;

Route::middleware('auth:api')->get('/books', function (Request $request) {
    return BookResource::collection(Book::all());
});
Creating a user

To be able to test the endpoint we have to create a user. We will be creating a command to do that for us. Let’s start by creating the command:

php artisan make:command CreateUserCommand

The command will create a user and a token based on some options that have to get passed to the command.

<?php

namespace App\Console\Commands;

use App\User;
use Illuminate\Console\Command;

class CreateUserCommand extends Command
{
    protected $signature = 'user:create {--name=} {--email=} {--password=} {--token=}';

    protected $description = 'Create user and token';

    public function handle()
    {
        $user = User::create([
            'name' => $this->option('name'),
            'email' => $this->option('email'),
            'password' => bcrypt($this->option('password'))
        ]);

        $token = $user->createToken($this->option('token'))->accessToken;

        $this->line($token);
    }
}

As you can see we have to pass four options to the command:

  • name
  • email
  • password
  • token, which is the name of the token

Create a user by executing the following command:

php artisan user:create --name=Laravel [email protected] --password=secret --token=LaravelToken

After executing the command you should see a token in your console. Copy this token.

Note:
Make sure that the token that gets printed in the console does not contain any line breaks.

Sending a request to the API

To send a GET request to the /api/books endpoint we have to add the token that you just copied as a Bearer token. To do that in Postman, click on the Authorization tab and select Bearer Token in the Type select box.

Furthermore, in the Headers tab add the Accept key with value application/json.

If the request was successful, you will receive a JSON array with books and status code 200.

Making an endpoint public

In the example above we used Laravel Passport for the API authentication. In some situations, you might want to have a public endpoint. Making an endpoint public is very easy. All you have to is remove the auth:api middleware from your route. This means that anybody can send a request to the endpoint without any form of authentication.

This how you set up an API with Laravel Passport. Please share your thoughts on this article. Make sure to check out my other posts as well. A lot of my content is about Laravel. Feel free to leave a comment if you have any feedback, questions or want me to write about another Laravel related topic.

Thank for read and keep visitting!