Good day, today we are going to be coding strictly in the backend, we are going to convert our Laravel 8 CRUD app to RESTful APIs. API is a software intermediary that allows two applications to talk to each other. Somethings you might need to create an app that can be run on different languages or frameworks, for example, you can use Laravel to create the backend for your application while the frontend might run on any JavaScript frameworks. API allows two or more programs to communicate with each other.
There are different types of APIs, but today we are going to concentrate on RESTful APIs. REST stands for Representational State Transfer, while API stands for Application Programming Interface. You can read more about API from the internet or other programming material.
Click on my profile to follow me to get more updates.
Normally, when we build an application, we need to secure the application so that unauthorized access will be blocked. Laravel already makes it easy to perform authentication via traditional login forms, but what about APIs? API does not maintain a session state between requests, so there is a need to use tokens to authenticate the app and also authorize the usage of the app.
Laravel provided a package that makes authentication of API very easy by using Laravel Passport, though, there are other options, this is the official packages by the Laravel team, this package provides a full OAuth2 server implementation. It is easy to apply and can be achieved in minutes. Without saying much, let's dive into it.
STEP 1: install laravel 8
To install the latest laravel framework, which is laravel 8.0 as of the time of publishing this article, run the command below
composer create-project --prefer-dist laravel/laravel laravel_8_api_crud
This will automatically create a laravel 8 app and some couple of things have been set up, we don’t need to copy and rename the env.example file, Laravel 8 does that automatically for us
Another important thing about Laravel 8, you don’t need to generate APP_KEY, this new version will also generate it for us.
With that all set up, our app is ready.
Step 2: Database setup
Create an empty database, Open the .env file, and update your database configurations.
For more information on this step, you can visit my previous article, where I explained the steps very well, Laravel 8 CRUD.
Step 3: Install Laravel Passport
let us install Laravel Passport, Passport service provider registers its own database migration directory, this means that it creates the table that we will be needing for storing clients. The table will be used to store the token generated which will be used to identify a currently authenticated user. This token will then be attached to every request allowing each user access to protected routes.
composer require laravel/passport
After installation, then we need to migrate, but before we run our migration command, we need to specify the default string length, else, we are going to run into errors. So go to app/Providers/AppServiceProvider.php and add this to the boot function
Schema::defaultstringLength(191);
also, add this to the top of the class
use Illuminate\Support\Facades\Schema;
Finally, we run our migration command
php artisan migrate
Step 4: Create the encryption keys
We need to create the encryption keys that are needed to generate our secure access tokens.
php artisan passport:install
This command will also create "personal access" and "password grant" clients which will be used to generate access tokens.
Step 5: Add the HasApiTokens trait to our user model
Go to App\Models\User.php and tell the User class to
use HasApiTokens,
Also add this to the top
use Laravel\Passport\HasApiTokens;
Step 6: Call the passport routes in AuthServiceProvider
Go to App/Providers/AuthServiceProvider.php and add
Passport::routes();
To the boot method, also add the path before the class at the top
use Laravel\Passport\Passport;
Uncomment the policy in the protected method of $policies
Step 7: Set the driver
This will be the final step in the setting up and configuration of Laravel\Passport, we going to change our api driver from the default token to passport.
Go to config\auth.php and locate the guards array. In the api key, change the driver from token to passport
Step 8: Create the Migration file for our CRUD api project
php artisan make:model Project -m
A migration file will be created in the database/migrations folder, and we need to create our schema, I added name (string), introduction (string), location (string), cost of the project (integer).
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateProjectsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('projects', function (Blueprint $table) {
$table->id();
$table->string('name', 255);
$table->string('introduction', 500)->nullable();
$table->string('location', 255)->nullable();
$table->integer('cost')->nullable();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('projects');
}
}
We need to update our Project Model so that it can be able to accept the fields. We need to add a protected $fillable method that will contain the fields that a user of the app can fill, this helps to prevent someone from hacking into the app through input fields.
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Project extends Model
{
use HasFactory;
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'name',
'location',
'introduction',
'cost',
];
/**
* The attributes that should be cast to native types.
*
* @var array
*/
protected $casts = [
'cost' => 'int',
];
}
Step 9: Migrate the new table
Step 10: Create a Resource
When building an API, the response will always be in JSON format, so we need a transformation layer that sits between our Eloquent models and the JSON responses. This is what will serve the response to the application’s user in a JSON format. Laravel provides us with a resource class that will help in transforming our models and the model collections into JSON. So we are going to create that
php artisan make:resource ProjectResource
This will create a folder in the app directory called Resources and also a file ProjectResource.php inside the resources.
Step 11: Create our Controllers
The Controller is responsible for the direction of the flow of data and an interface between the user and the database and views. In this case, we are not interacting with views now because we are dealing with API, so our response will be in JSON format. The standard for RESTful APIs is to send the response in JSON.
We are going to be creating two controllers, the first will be the Authentication Controller and the second is our Project Controller, we need the Authentication Controller in order to generate the token to use in Project Controller.
php artisan make:controller API/AuthController
This will create a folder called API in App/Http/Controllers. It will also create a new file called AuthController.php. Click on AuthController.php and update it with the following code.
<?php
namespace App\Http\Controllers\API;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use App\Models\User;
use Illuminate\Support\Facades\Hash;
class AuthController extends Controller
{
public function register(Request $request)
{
$validatedData = $request->validate([
'name' => 'required|max:55',
'email' => 'email|required|unique:users',
'password' => 'required|confirmed'
]);
$validatedData['password'] = Hash::make($request->password);
$user = User::create($validatedData);
$accessToken = $user->createToken('authToken')->accessToken;
return response(['user' => $user, 'access_token' => $accessToken], 201);
}
public function login(Request $request)
{
$loginData = $request->validate([
'email' => 'email|required',
'password' => 'required'
]);
if (!auth()->attempt($loginData)) {
return response(['message' => 'This User does not exist, check your details'], 400);
}
$accessToken = auth()->user()->createToken('authToken')->accessToken;
return response(['user' => auth()->user(), 'access_token' => $accessToken]);
}
}
In our AuthController, we created two methods: register and logic methods
In the register method, we use the Laravel Validate method to make sure that the name, email, and password is provided, this will also make sure that the email has not been taken and is a valid email address, the password must be confirmed before the user will be added.
After the validation, we use hash to encrypt the password before creating the user, we can't store plain password, lastly, we grab the access token and return it with the user’s information.
In the login method, we also validate the data been pass, to make sure the email and password are submitted, if the data did not correspond to any user, it will return a message that the user does not exist, if it corresponds, then it returns the user and the access token.
Let us create our ProjectController in the API using --api switch.
php artisan make:controller API/ProjectController --api --model=Project
The --api switch will create our Controller without the create and edit methods, those methods will present HTML templates.
Go to App/Http/Controller/API, and click on ProjectController, copy the code below and update the methods.
<?php
namespace App\Http\Controllers\API;
use App\Http\Controllers\Controller;
use App\Models\Project;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
use App\Http\Resources\ProjectResource;
class ProjectController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
$projects = Project::all();
return response([ 'projects' => ProjectResource::collection($projects), 'message' => 'Retrieved successfully'], 200);
}
/**
* Store a newly created resource in storage.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
$data = $request->all();
$validator = Validator::make($data, [
'name' => 'required|max:255',
'description' => 'required|max:255',
'cost' => 'required'
]);
if ($validator->fails()) {
return response(['error' => $validator->errors(), 'Validation Error']);
}
$project = Project::create($data);
return response(['project' => new ProjectResource($project), 'message' => 'Created successfully'], 201);
}
/**
* Display the specified resource.
*
* @param \App\Models\Project $project
* @return \Illuminate\Http\Response
*/
public function show(Project $project)
{
return response(['project' => new ProjectResource($project), 'message' => 'Retrieved successfully'], 200);
}
/**
* Update the specified resource in storage.
*
* @param \Illuminate\Http\Request $request
* @param \App\Models\Project $project
* @return \Illuminate\Http\Response
*/
public function update(Request $request, Project $project)
{
$project->update($request->all());
return response(['project' => new ProjectResource($project), 'message' => 'Update successfully'], 200);
}
/**
* Remove the specified resource from storage.
*
* @param \App\Models\Project $project
* @return \Illuminate\Http\Response
*/
public function destroy(Project $project)
{
$project->delete();
return response(['message' => 'Deleted']);
}
}
- The index method will retrieve all the projects in the database with a success message (Retrieved successfully) and returns a status code of 200.
- The store method will validate and store a new project, just like the AuthController, and returns a status code of 201, also a message of "Created successfully".
- The show method will retrieve just one project that was passed through the implicit route model binding, and also returns an HTTP code of 200 if successful.
- The update method receives the HTTP request and the particular item that needs to be edited as a parameter. It updates the project and returns the appropriate response.
- The destroy method also receives a particular project through implicit route model binding and deletes it from the database.
Step 12: Create our Routes
Go to routes folder and click on api.php, and updates with the following code
<?php
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\API\AuthController;
use App\Http\Controllers\API\ProjectController;
/*
|--------------------------------------------------------------------------
| 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();
});
Route::post('register', [AuthController::class, 'register']);
Route::post('login', [AuthController::class, 'login']);
Route::apiResource('projects', ProjectController::class)->middleware('auth:api');
We added a register route and login routes which are post routes and also an apiResource route for projects utilizing the auth:api middleware.
Step 13: Testing
We are going to be testing our API with Postman, due to complexity and time, I might not be able to explain everything on Postman, but observe the red squares on this page
register a user
trying to get a user that does not exist
Login a registered user
In other to access the projects routes, we must authenticate the user, in other to achieve this, we must add the user's token.
Copy the access token generated for the user when the user login in, click on Authorization on postman and select the type, Bearer Token, and paste the token by the input field by the right.
Retrieve all the projects, but no project was created
Create a project
Retrieve all the projects
Retrieve just 1 project
Update a project
Delete a project
Finally, we have come to the end of the article, if you follow the article religiously, you will not make a mistake, but in case you can't follow all through, this is the link to the repo
You can encourage me by clicking the heart button to show love, you are can also leave a comment, suggestion, etc. You are also free to contact me through any of my contact details.
click the link to view my profile and follow me
Visit my other posts
Top comments (23)
You have not told us how to deal with access token because when I entered data in 127.0.0.1:8000/api/register and in 127.0.0.1:8000/api/projects route it shows login route not found and after adding this header X-Requested-With I am getting unauthorized message and on register link it's showing response to unauthorized access ?
how to deal with this I have 0 rows in user db ? show I make fake data first to generate access token
You should have modified the api routes not the web routes. I made such mistake at the beginning that resulted none of the url existed.
When I carefully corrected two typos in my scripts following the tutorial it worked.
I agree, its confusing to say the least for beginner how to use postman in particular.
I am having the same problem none of this url can be found.
surely, I am modified routes on api.php .
Now I figured out your first post is creating mess because you do not use token in this route 127.0.0.1:8000/api/projects this is wrong implementation as it is not using using token we make with passport auth.
Hi Mian, could you please give more details of this? It worked okay as I tried.
Target class [App\Http\Controllers\App\Http\Controllers\API\ProjectController] does not exist
please help me
Hi @vincenttetteh ! In case you're still having this issue, you may refer to this link:
stackoverflow.com/questions/638079...
We have the same problem and this solved mine
In header please add Key: Accept Value: application/json
If composer dumpautoload is not helping then check if you have proper namespace declaration in ProjectController.php and double check for typos in class name/route declaration.
Hello, do you decide this problem?
testingProjectLaravel % php artisan migrate
Illuminate\Database\QueryException
SQLSTATE[HY000] [1049] Unknown database 'testingprojectlaravel' (SQL: select * from information_schema.tables where table_schema = testingProjectLaravel and table_name = migrations and table_type = 'BASE TABLE')
at vendor/laravel/framework/src/Illuminate/Database/Connection.php:678
674▕ // If an exception occurs when attempting to run a query, we'll format the error
675▕ // message to include the bindings with SQL, which will make this exception a
676▕ // lot more helpful to the developer instead of just the database's errors.
677▕ catch (Exception $e) {
➜ 678▕ throw new QueryException(
679▕ $query, $this->prepareBindings($bindings), $e
680▕ );
681▕ }
682▕
34 artisan:37
Illuminate\Foundation\Console\Kernel::handle(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
Your database is not set property or you have not any database with this name testing.......
How to logout?
I got it
public function logout (Request $request) {
$accessToken = auth()->user()->token();
$token= $request->user()->tokens->find($accessToken);
$token->revoke();
This makes sense I however got this error, do not know why,
Call to a member function token() on null
I figured out what happened. Before you can log a user out via API, you will need to pass authentication first and then log your login off. Therefore the route
logout
has to be underneath the middleware auth. For convenience I grouped it with the/user
route that is used in this tutorial.Thankyou Sir
request project id=1 GET api.test/api/projects/1
But, how to request by project name? can you give me a sample?
Thank you.
after I`ve done all by hands I got an error. Please help)
Symfony\Component\Routing\Exception\RouteNotFoundException
Route [login] not defined.
192.168.20.105:8085/api/projects
same here. Did you figure it out?
this works for me - its important to register a user first and get their token back and use it in future requests.
can it be a security issue that exposing the id in resource?