Hello friends, today we will build REST API using Lumen a micro php framework by laravel. We already covered about the basics of REST Architecture in my previous post. So in this post we will not talk about the basics of REST API, and we will directly start building REST API. And after building REST API we will build the app in android platform.
So without wasting time lets build our REST API.
Table of Contents
- 1 Build REST API using Lumen Video Tutorial
- 2 Tools Required
- 3 Database Design
- 4 Creating Lumen Project
- 5 Lumen Directory Structure
- 6 Setting Up Database
- 7 Building Database Schema
- 8 Making Models
- 9 Making Controllers
- 10 Testing the API
- 11 Adding Basic Authentication
- 12 Things I Left in this API
- 13 Build REST API using Laravel Lumen Source Code
Build REST API using Lumen Video Tutorial
- If you don’t want to read the post you can go to this video explanation as well.
- If you are ok with written post then lets move ahead.
Tools Required
We will be using the following to Build our RESTful Web Services.
- PHP Storm (Though you can use any other IDE but I will recommend using it).
- Lumen by Laravel (The php framework)
- XAMPP (For the dev environment)
- POSTMAN (Rest client for testing the API)
I hope you got all the tools. Now lets discuss the database design.
Database Design
- Here is my database design. I am going to build a very simple Question Answer Application. So keeping it very simple this is the database model I finalized.
- This time you don’t need an SQL code for the database. Lumen will do it for you. Don’t get confused just keep reading 😉
Creating Lumen Project
- Make sure you have composer installed in your computer.
- Now go to the directory htdocs of xampp and open command window in the same directory. (you can use shift + right click to open command window in a specified folder).
- Now run the following command.
1 2 3 |
composer create-project laravel/lumen MyQAApp |
- In the above command MyQAApp is the name of your project. If you want you can change it to anything you like.
- After running the command wait for sometime (it may take very long if you have a slow internet connection).
- After the above command finishes. You will find a folder with the project name created at htdocs.
- Now go to web browser and try opening localhost/MyQAApp/public/Â (make sure xampp is running and you are using the same project name).
- You need to open this folder in PHP Storm as a project.
Lumen Directory Structure
- Once you have opened the project in PHP Storm you will see the following directory structure.
- You see many folders here but we need to care about only the following things.
app:Â Here we will define models to perform database operations.
app/http/controllers: Here we will define our app logics. Â
app/http/middlewares: We will define the API security here.
database/migrations:Â We will define the database schema here.
routes: Here we will define the endpoints for the API.
Setting Up Database
- First create a database in phpmyadmin. You do not need to create any tables. Just create a database. (I have created a database named myqaapp).
- Now come back to project in php storm and open .env file, and modify it as below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
APP_ENV=local APP_DEBUG=true APP_KEY= APP_TIMEZONE=UTC DB_CONNECTION=mysql DB_HOST=127.0.0.1 DB_PORT=3306 DB_DATABASE=myqaapp DB_USERNAME=root DB_PASSWORD= CACHE_DRIVER=array QUEUE_DRIVER=sync |
- I have changed the following, in the above file.
DB_DATABASE=yourdatabasename
DB_USERNAME=database username of xampp (root in this case)
DB_PASSWORD= database password (nothing in this case)
CACHE_DRIVER=array - Now we will create database schema in migrations.
Building Database Schema
- On PHP Storm go to View->Tool Windows->Terminal.Â
- You will see the terminal window in the bottom.
Creating Migrations
- On the terminal execute the following command.
1 2 3 |
php artisan make:migration create_users_table --create=users |
- In the above command users is the name of the table. You need to perform the above command for every table that we designed in our database. Make sure you create the migrations in the following sequence. The sequence is important as we are dealing with primary and foreign keys.
- In these files we will design the database schema.
Designing Database Schema
Users Schema
- Lets start with the first one users_table.php. You will see the following code inside.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
<?php use Illuminate\Support\Facades\Schema; use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class CreateUsersTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('users', function (Blueprint $table) { $table->increments('id'); $table->timestamps(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists('users'); } } |
- Inside the method up() we will define the schema. We have already designed the database so we just need to add few lines inside the method up().
- Modify the code as below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
<?php use Illuminate\Support\Facades\Schema; use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class CreateUsersTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('users', function (Blueprint $table) { $table->increments('id'); //these 3 fields added $table->string('username'); $table->text('password'); $table->string('email'); $table->timestamps(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists('users'); } } |
- The same way we will define the schema for others table as well. In the above code you can see we have written $table->string(’email’);Â this means the table has a new column name email with datatype string. You can check the official docs of laravel for the list of available datatypes.
- The same way lets define schema for remaining 3 tables.
Categories Schema
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
<?php use Illuminate\Support\Facades\Schema; use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class CreateCategoriesTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('categories', function (Blueprint $table) { $table->increments('id'); $table->string('name'); $table->timestamps(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists('categories'); } } |
Questions Schema
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
<?php use Illuminate\Support\Facades\Schema; use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class CreateQuestionsTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('questions', function (Blueprint $table) { $table->increments('id'); $table->string('question'); $table->integer('category_id')->nullable()->unsigned(); $table->integer('user_id')->unsigned(); $table->foreign('category_id')->references('id')->on('categories'); $table->foreign('user_id')->references('id')->on('users'); $table->timestamps(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists('questions'); } } |
Answers Schema
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
<?php use Illuminate\Support\Facades\Schema; use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class CreateAnswersTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('answers', function (Blueprint $table) { $table->increments('id'); $table->string('answer'); $table->integer('user_id')->unsigned(); $table->integer('question_id')->unsigned(); $table->foreign('user_id')->references('id')->on('users'); $table->foreign('question_id')->references('id')->on('questions'); $table->timestamps(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists('answers'); } } |
- So we have the database schema ready. Now lets do a migration to create the tables in MySQL as well.
Making Migration
- In the terminal type php artisan migrate. And the database will be migrated automatically.
- Now check PhpMyAdmin and you will see the tables are created.
Making Models
- Models will simplify the database. We will use the Laravel Eloquent ORM and Models and it will simplify the database operation like never before. You actually do not need to write a single line of SQL query if you are going to use it.
- But the first thing is we need to enable the Eloquent, as it is not enabled by default.
Enabling Eloquent
- Go to bootstrap/app.php. and uncomment the following lines.
- Now for each database table we will create a model inside app folder. So we will create User.php, Categorie.php, Question.php and Answer.php.
User Model
- Come inside the User.php and modify the model class as below. You can see below we are also defining the relationships.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<?php namespace App; use Illuminate\Database\Eloquent\Model; class User extends Model { public function questions(){ return $this->hasMany('App\Question'); } } |
Question Model
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<?php namespace App; use Illuminate\Database\Eloquent\Model; class Question extends Model { public function answers(){ return $this->hasMany('App\Answer'); } } |
Category Model
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<?php namespace App; use Illuminate\Database\Eloquent\Model; class Category extends Model { public function questions(){ return $this->hasMany('App\Question'); } } |
Answer Model
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<?php namespace App; use Illuminate\Database\Eloquent\Model; class Answer extends Model { } |
- So we have defined the models. Now lets build controllers.
Making Controllers
- Now we will make the controllers. These controllers will perform the actual operations. All controllers file will be created inside app/http/controllers.
User Controller
- Create a new php file named UserController.php. First we need to think what operation we need for a user. So for this app I have decided 3 operations.
- User Registration: A new user can register to the application.
- User Login: User can login to the application by providing username and password.
- Get My Questions: User can view all the questions that is asked by that user.
- So Inside UserController.php we will define a class that will extend Controller, and inside the class we will define all the operation as separate functions.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
<?php namespace App\Http\Controllers; use Illuminate\Hashing\BcryptHasher; use Illuminate\Support\Facades\Validator; use Illuminate\Http\Request; use App\User; class UserController extends Controller { //this function is used to register a new user public function create(Request $request) { //creating a validator $validator = Validator::make($request->all(), [ 'username' => 'required|unique:users', 'password' => 'required', 'email' => 'required|unique:users' ]); //if validation fails if ($validator->fails()) { return array( 'error' => true, 'message' => $validator->errors()->all() ); } //creating a new user $user = new User(); //adding values to the users $user->username = $request->input('username'); $user->email = $request->input('email'); $user->password = (new BcryptHasher)->make($request->input('password')); //saving the user to database $user->save(); //unsetting the password so that it will not be returned unset($user->password); //returning the registered user return array('error' => false, 'user' => $user); } //function for user login public function login(Request $request) { $validator = Validator::make($request->all(), [ 'username' => 'required', 'password' => 'required' ]); if ($validator->fails()) { return array( 'error' => true, 'message' => $validator->errors()->all() ); } $user = User::where('username', $request->input('username'))->first(); if (count($user)) { if (password_verify($request->input('password'), $user->password)) { unset($user->password); return array('error' => false, 'user' => $user); } else { return array('error' => true, 'message' => 'Invalid password'); } } else { return array('error' => true, 'message' => 'User not exist'); } } //getting the questions for a particular user public function getQuestions($id) { $questions = User::find($id)->questions; foreach ($questions as $question) { $question['answercount'] = count($question->answers); unset($question->answers); } return array('error' => false, 'questions' => $questions); } } |
Question Controller
- For Question we will perform the following operations.
- Submit a new Question.
- Get all the questions.
- Get questions of an specified category.
- So create a file named QuestionController.php and write the following code.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
<?php namespace App\Http\Controllers; use Illuminate\Database\Eloquent\ModelNotFoundException; use Illuminate\Support\Facades\Validator; use Illuminate\Http\Request; use App\Question; use App\Category; class QuestionController extends Controller { public function create(Request $request){ $validator = Validator::make($request->all(), [ 'question'=>'required', 'user_id'=>'required' ]); if($validator->fails()){ return array( 'error' => true, 'message' => $validator->errors()->all() ); } $question = new Question; $question->question = $request->input('question'); $question->user_id= $request->input('user_id'); $question->category_id= $request->input('category_id'); $question->save(); return array('error'=>false, 'question'=>$question); } public function getAll(){ $questions = Question::all(); foreach($questions as $question){ $question['answercount'] = count($question->answers); unset($question->answers); } return array('error'=>false, 'questions'=>$questions); } public function getByCategory($category_id){ try{ $questions = Category::findOrFail($category_id)->questions; foreach($questions as $question){ $question['answercount'] = count($question->answers); unset($question->answers); } return array('error'=>false, 'questions'=>$questions); }catch(ModelNotFoundException $e){ return array('error'=>true, 'message'=>'Invalid Category ID'); } } } |
Category Controller
- Create a new file named CategoryController.php and write the following code.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
<?php namespace App\Http\Controllers; use Illuminate\Support\Facades\Validator; use Illuminate\Http\Request; use App\Category; class CategoryController extends Controller { public function create(Request $request){ $validator = Validator::make($request->all(), [ 'name'=>'required|unique:categories' ]); if($validator->fails()){ return array( 'error' => true, 'message' => $validator->errors()->all() ); } $category = new Category; $category->name = $request->input('name'); $category->save(); return array('error'=>false, 'category'=>$category); } } |
- Finally lets create the AnswerController.
Answer Controller
- Again create a new file named AnswerController.php and write the following code.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
<?php namespace App\Http\Controllers; use Illuminate\Support\Facades\Validator; use Illuminate\Http\Request; use App\Answer; class AnswerController extends Controller { public function submit(Request $request){ $validator = Validator::make($request->all(), [ 'answer'=>'required', 'user_id'=>'required', 'question_id'=>'required' ]); if($validator->fails()){ return array( 'error' => true, 'message' => $validator->errors()->all() ); } $answer = new Answer; $answer->answer = $request->input('answer'); $answer->question_id= $request->input('question_id'); $answer->user_id= $request->input('user_id'); $answer->save(); return array('error'=>false, 'answer'=>$answer); } } |
- Our controllers are ready, now we will define URL routes to call the operations.
Creating URL EndPoints
- Come inside routes/web.php. And write the following code.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
<?php /* |-------------------------------------------------------------------------- | Application Routes |-------------------------------------------------------------------------- | | Here is where you can register all of the routes for an application. | It is a breeze. Simply tell Lumen the URIs it should respond to | and give it the Closure to call when that URI is requested. | */ $app->get('/', function () use ($app) { return $app->version(); }); /*** * The first parameter 'createuser' it is the url address * So to call this we will use the URL as BASE URL + createuser * * The second parameter is UserController@create * it means we are calling create function which is inside UserController * * Using the same for all routes ***/ $app->post('createuser', 'UserController@create'); $app->post('createquestion', 'QuestionController@create'); $app->post('submitanswer', 'AnswerController@submit'); $app->post('userlogin', 'UserController@login'); $app->post('createcategory', 'CategoryController@create'); $app->get('categories', 'CategoryController@get'); $app->get('getmyquestions/{user_id}', 'UserController@getQuestions'); $app->get('questions', 'QuestionController@getAll'); $app->get('questions/{category_id}', 'QuestionController@getByCategory'); |
Testing the API
- Now lets test all the endpoints. For this I will be using POSTMAN you can use any REST Client.
- So open postman. And write a URL. In my case the BASE URL is http://localhost/MyQAApp/public/ and we will add what we created after this.
- So lets test createuser first, for this put the URL in POSTMAN, select request type as POST and add required parameters. (You can take help from the below screenshot).
- You can see it is working fine. The same way you need to test all the other routes as well.
Adding Basic Authentication
- Now imagine we are using this API for our application. And someone gets the URL addresses of our API. Then what can happen? With the URL address our database can be flooded with spam values. So we need some authentication part so that we can accept only the request from the application.
- There are many security models but here to keep it simple I will tell you about adding Basic Authentication.
Creating a MiddleWare
- Come inside app/http/middleware and open the file Authenticate.php, remove everything and write the following code.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
<?php namespace App\Http\Middleware; use Closure; //defining a username and password define('USERNAME','belalkhan'); define('PASSWORD', '123456'); class Authenticate { public function handle($request, Closure $next) { //getting values from headers //if the user is authenticated accepting the request if($request->header('PHP_AUTH_USER') == USERNAME && $request->header('PHP_AUTH_PW') == PASSWORD){ return $next($request); //else displaying an unauthorized message }else{ $content = array(); $content['error'] = true; $content['message'] = 'Unauthorized Request'; return response()->json($content, 401); } } } |
- Now we will add this middleware to all routes. To do this come inside bootstrap/app.php. And add the following code inside Register Middleware section.
1 2 3 4 5 |
$app->middleware([ App\Http\Middleware\Authenticate::class ]); |
- Now try sending the request in POSTMAN again. You will see the following output.
- As you can see the error message that the request is an Unauthorized Request with status code 401. So our API is little secured now. As to send the request we need a username and password to authorize every request.
Sending Request with Basic Auth
- Postman has an inbuilt option to set Basic Auth headers. Just click on Authorization and select Basic Auth.
- Now enter your API username and password that you have set, and send the request.
- You can see now it is working fine.
Things I Left in this API
- I left many things in this API as it is an example only to teach you the idea of building an API. I am mentioning some tasks that I left to make it simple. But if you want to learn you can try completing this API.
- No user update option.
- No pagination if there will be larger data it is not a good idea to fetch all at once.
- No upvote and likes on answers.
- I pointed out some points, there may still be something that can be added. So comment below that what should be added on this API.
Build REST API using Laravel Lumen Source Code
- If you are facing troubles you can get my source code from the below link.
[sociallocker id=1372] Build REST API using Laravel/Lumen Source Code Download [/sociallocker]
So that all for this Build REST API Tutorial friends. I hope you got the idea of building RESTful Web Services.
In the next post we will build an Android Application using the new Kotlin Language for this API.
Meanwhile iff you are having any troubles or confusions, feel free to leave your comments. Also don’t forget to SHARE if you found this article helpful. Thank You 🙂