X
    Categories: API Tutorials and Use CasesLearn Laravel Tutorials, Tips And Guides

Create a REST API for ToDo App with Authentication Using Lumen

In the first installment of the series on creating a ToDo App with Laravel 5.4, I added an API_KEY column in the user table and commented that I might use it in the future. I will use it in this second installment of the series.

In this article, I am going to use the same tables that I created in the ToDo App. I will connect it with Lumen and create a REST API. This API will feature:

  • An API Key will be generated and sent to the user, once a login request is sent to the API.
  • In order to perform CRUD operations, the user needs to add this API key in the authorization header. If the API key is not valid, the request will not proceed.

The complete code for this tutorial can be found on Github repo.

Create a New Lumen Project

I will start with creating a new Lumen project for the ToDo REST API. For this, run the following command:

composer create-project --prefer-dist laravel/lumen todorest

Wait for a composer to create the Lumen project. Now edit the .env file and update it with the database information. I am going to use the same tables that I have created in first part of the ToDo app.

Now, I need to setup Lumen to use Facade and Eloquent. For this, head to the bootstrap folder and open app.php file. In the file, uncomment the following lines:

$app->withFacades();
$app->withEloquent();
$app->register(App\Providers\AppServiceProvider::class);
$app->register(App\Providers\AuthServiceProvider::class);
$app->routeMiddleware([
'auth' => App\Http\Middleware\Authenticate::class,
]);

Create the User Model

Now head to the app folder and create a new file. Name it Users.php and paste the following code in it.

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

use Illuminate\Contracts\Auth\Authenticatable;

use Illuminate\Auth\Authenticatable as AuthenticableTrait;


class Users extends Model implements Authenticatable

{

   //

   use AuthenticableTrait;

   protected $fillable = ['username','email','password','userimage'];

   protected $hidden = [

   'password'

   ];

   /*

   * Get Todo of User

   *

   */

   public function todo()

   {

       return $this->hasMany('App\Todo','user_id');

   }

}

In the Users model, I defined its relationship with the Todo Table along with foreign id.

Create a ToDo Model

Now, in the same folder, create a new file and name it Todo.php. Now paste the following code in it.

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Todo extends Model

{

   //

   protected $table = 'todo';

   protected $fillable = ['todo','category','user_id','description'];

}

 

Create the User Controller

Now go to app/Http/Controllers and create a new file with the name UsersController.php. Paste the following code in it:

<?php

namespace App\Http\Controllers;

use App\Http\Controllers\Controller;

use Illuminate\Support\Facades\Hash;

use Illuminate\Http\Request;

use App\Users;

class UsersController extends Controller

{

  public function __construct()

   {

     //  $this->middleware('auth:api');

   }

   /**

    * Display a listing of the resource.

    *

    * @return \Illuminate\Http\Response

    */

   public function authenticate(Request $request)

   {

       $this->validate($request, [

       'email' => 'required',

       'password' => 'required'

        ]);

      $user = Users::where('email', $request->input('email'))->first();

     if(Hash::check($request->input('password'), $user->password)){

          $apikey = base64_encode(str_random(40));

          Users::where('email', $request->input('email'))->update(['api_key' => "$apikey"]);;

          return response()->json(['status' => 'success','api_key' => $apikey]);

      }else{

          return response()->json(['status' => 'fail'],401);

      }

   }

}    

?>

In this controller, I created an authenticate model which checks whether the user is valid. If the user is valid, it returns an API key. If not, it returns a fail error with the response code 401.

Update the Auth Service Provider

Now, head to the app/Providers and open AutheServiceProvider.php file. In this file, I will check the validity of the API key that the user sends in for performing ToDo operations.

In the file, at the top of the code, after namespace, add the following line:

use App\Users;

Now go to the `boot()` method and replace the code in it with the following:

$this->app['auth']->viaRequest('api', function ($request) {
if ($request->header('Authorization')) {
$key = explode(' ',$request->header('Authorization'));
$user = Users::where('api_key', $key[1])->first();
if(!empty($user)){
$request->request->add(['userid' => $user->id]);

}
return $user;
}
});

In this code, I will check that the user have a valid API key and then append the verified userid to the request. If the API is not verified, it will return Unauthorized. In order to customized this message, head to the app/Http/Middleware folder and open Authenticate.php file. In this file, go to the `handle()` method and change the line inside the `if` with the following.

return response()->json(['error' => 'Unauthorized'], 401);

The next action item is the creation of the routes.

Create the ToDo Controller

Now in the Controllers folder, create a new file and name it TodoController.php. Paste the following code in it:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Todo;
use Auth;

class TodoController extends Controller
{
    /**
     * Create a new controller instance.
     *
     * @return void
     */
    public function __construct()
    {
        $this->middleware('auth');
    }
    
    /**
     * Display a listing of the resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function index(Request $request)
    {
        $todo = Auth::user()->todo()->get();
        return response()->json(['status' => 'success','result' => $todo]);
    }

    /**
     * Store a newly created resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function store(Request $request)
    {
        $this->validate($request, [
        'todo' => 'required',
        'description' => 'required',
        'category' => 'required'
         ]);
        if(Auth::user()->todo()->Create($request->all())){
            return response()->json(['status' => 'success']);
        }else{
            return response()->json(['status' => 'fail']);
        }

    }

    /**
     * Display the specified resource.
     *
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function show($id)
    {
        $todo = Todo::where('id', $id)->get();
        return response()->json($todo);
        
    }

    /**
     * Show the form for editing the specified resource.
     *
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function edit($id)
    {
        $todo = Todo::where('id', $id)->get();
        return view('todo.edittodo',['todos' => $todo]);
    }

    /**
     * Update the specified resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function update(Request $request, $id)
    {
        $this->validate($request, [
        'todo' => 'filled',
        'description' => 'filled',
        'category' => 'filled'
         ]);
        $todo = Todo::find($id);
        if($todo->fill($request->all())->save()){
           return response()->json(['status' => 'success']);
        }
        return response()->json(['status' => 'failed']);
    }

    /**
     * Remove the specified resource from storage.
     *
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function destroy($id)
    {
        if(Todo::destroy($id)){
             return response()->json(['status' => 'success']);
        }
    }
}

In this code, I am simply performing CRUD operation once the user have a valid API key. The auth middleware tests whether the user have a valid API key. I have created a different validation method in it; one is for creating a new ToDo item, which is the `store()` method in which I am validating all the required fields. But in the `edit()` method, I am only testing if that field is in parameter and that it shouldn’t be empty.

Create Routes For the Todo API

Now go to the routes folder and open the web.php file. Inside this file, add the following routes.

$app->group(['prefix' => 'api/'], function ($app) {
$app->get('login/','UsersController@authenticate');
$app->post('todo/','TodoController@store');
$app->get('todo/', 'TodoController@index');
$app->get('todo/{id}/', 'TodoController@show');
$app->put('todo/{id}/', 'TodoController@update');
$app->delete('todo/{id}/', 'TodoController@destroy');
});

All the hard work is done. I will now test it!

Test the ToDo REST API

For testing the REST API, I will use PostMan. Before getting started with the testing, run the following command inside the Lumen app folder to start a server.

php -S localhost:8000 -t public

Login using the API

Now in the PostMan, try to login with the email and password you used for the ToDo app.

Send a GET request to: http://localhost:8000/api/login/ along with the email and password parameter

You will receive a Success message along with the API Key.

Get All the ToDo Items

I will now try to get all the ToDo items of a user, without adding an API key. For this, send a GET request to: http://localhost:8000/api/todo

You will get an error of Unauthorized. Now I will add the API key in the Authorization header, and then send the request again.

This time, you will get all the ToDo items of the user.

Create a New ToDo Item

I will now create a new ToDo item. For this, send a POST request to: http://localhost:8000/api/todo along with todo, description and category parameters. Remember to add the API key in the header or else you will get the error message.

If all fields are filled, you will get the Success message.

I will now test validation by trying to send a ToDo create request with an empty description field.

As expected, you will get an error.

Edit a ToDo

To edit a todo send a PUT request to http://localhost:8000/api/todo/{id} along with the todo,description and category parameters you like to edit.

Delete a ToDo Item

I will now delete a ToDo item. Send a DELETE request to http://localhost:8000/api/todo/{id}.

Now, when you try to get all ToDo items, you will see that the particular item has been deleted.

Final Words

This is the end of this tutorial series. The complete code can be found on Github. If you are unable to understand anything or would like to contribute to the discussion, do leave a comment below.

Ahmed Khan: Ahmed was a PHP community expert at Cloudways - A Managed PHP Hosting Cloud Platform. He is a software engineer with extensive knowledge in PHP and SEO. He loves watching Game of Thrones is his free time. Follow Ahmed on Twitter to stay updated with his works. You can email him at ahmed.khan@cloudways.com