In this tutorial, we will create a simple RESTFUL API using Laravel Version 6. I assume you already familiar with create a new Laravel project and connect to the database. Afterwards, follow these steps together :
Install Laravel Passport
1 |
composer require laravel/passport |
Run migration
1 |
php artisan migrate |
We will find additional tables on the database :
Run this command to get Laravel personal access client and laravel password grant client in oauth_clients table.
1 |
php artisan passport:install |
The next step is to add HasApiTokens to User.php model :
1 2 3 4 5 |
use Laravel\Passport\HasApiTokens; class User extends Authenticatable { use Notifiable, HasApiTokens; |
config/auth.php
Change the api driver from token (default) to passport :
1 2 3 4 5 6 7 8 9 10 11 12 |
'guards' => [ 'web' => [ 'driver' => 'session', 'provider' => 'users', ], 'api' => [ 'driver' => 'passport', 'provider' => 'users', 'hash' => false, ], ], |
AuthServiceProvider.php
At the boot() method, add Passport route:
1 2 3 4 5 6 |
public function boot() { $this->registerPolicies(); Passport::routes(); } |
This will add additional routes to our application. We will be able to see the routes by type this command to the terminal :
1 |
php artisan route:list --name=passport |
kernel.php
1 2 |
protected $middleware = [ \Laravel\Passport\Http\Middleware\CreateFreshApiToken::class, |
This line of code will make passport do the authorization under the hood, so we don’t need to put Authorization Bearer everytime to authenticate api requests.
Requesting a Passport Token
In this session we will see how to handle the access token.
Create route to get the token :
1 |
Route::post('/token', 'Auth\LoginController@getToken'); |
Modify LoginController.php and add the getToken method :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public function getToken(Request $request) { $request->request->add([ 'grant_type' => 'password', 'client_id' => 2, 'client_secret' => 'gdTRLQI2AVKyeGcGXpC4fuOdsIwWi1uwRFGKqUfz', 'username' => $request->username, 'password' => $request->password, ]); $requestToken = Request::create(env('APP_URL') . '/oauth/token', 'post'); $response = Route::dispatch($requestToken); return $response; } |
Don’t forget to use the following methods :
1 2 |
use Illuminate\Http\Request; use Illuminate\Support\Facades\Route; |
Modify your .env file and change the APP_URL to your url. For example, http://localhost:8000.
We got the client_id and client_secret from oauth_clients table that we have created previously.
Try the Endpoint using Postman
Install and open postman, then using POST method open the endpoint, add Accept – application/json to your header, and on the body, add the username and password :
If everything goes well, you will get the access_token as the response. When we perform the request on the web, later we don’t need to copy and paste this access_token as we already put CreateFreshApiToken inside the kernel.php.
API Endpoint to GET All Records
Here I already have a post table :
Later we will put this information to postman.
Create PostController inside API folder :
1 |
php artisan make:controller Api/PostController -m Post --api |
Open routes/api.php
1 |
Route::get('/posts', 'Api\PostController@index'); |
Api\PostController.php
1 2 3 4 5 |
public function index() { $posts = Post::paginate(20); return $posts; } |
The result can be seen in your browser :
Eloquent API Resource
Before Laravel 5.5, we usually use Fractal as an transformation layer. However, since version 5.5, we can use eloquent api resource.
Create Resource from the terminal :
1 2 |
php artisan make:resource PostResource php artisan make:resource UserResource |
Post table related to user table through author_id column. Therefore, create the relation on Post.php and User.php :
Post.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<?php namespace App; use Illuminate\Database\Eloquent\Model; class Post extends Model { protected $table = 'posts'; protected $fillable = ['title', 'slug', 'body']; public function user() { return $this->belongsTo(User::class, 'author_id', 'id'); } } |
User.php
1 2 3 4 |
public function posts() { return $this->hasMany(Post::class, 'author_id', 'id'); } |
UserResource.php
1 2 3 4 5 6 7 8 |
public function toArray($request) { return [ 'id' => $this->id, 'name' => $this->name, 'email' => $this->email, ]; } |
PostResource.php
1 2 3 4 5 6 7 8 9 10 |
public function toArray($request) { return [ 'id' => $this->id, 'title' => $this->title, 'url' => $this->slug, 'user' => new UserResource($this->user), 'excerpt' => $this->excerpt, ]; } |
Modify PostController.php
1 2 3 4 5 |
public function index() { $posts = Post::with('user')->paginate(20); return PostResource::collection($posts); } |
The result can be seen on your browser with the better json structure.
Endpoint to POST New Record
api.php
Create route for apiResource :
1 2 3 |
Route::middleware('auth:api')->group(function () { Route::apiResource('/posts', 'Api\PostController')->except('index'); }); |
To add some rule, let’s create a new request
1 |
php artisan make:request PostStoreRequest |
PostStoreRequest.php
1 2 3 4 5 6 7 8 9 10 11 12 |
public function authorize() { return true; } public function rules() { return [ 'title' => 'required', 'body' => 'required', ]; } |
PostController.php
At the store method, add the following command to add the new post :
1 2 3 4 5 6 7 8 9 |
public function store(PostStoreRequest $request) { $post = $request->user()->posts()->create($request->only('title', 'slug', 'body')); return response()->json([ 'message' => "Your question has been submitted", 'post' => new PostResource($post), ], 201); } |
This will return the message and post in json format.
Open postman, POST the api url and put title, slug, and body in the body part :
Update and Delete Record using PUT and DELETE
Create Policy
Before we update and delete the post, there is one middleware that we need to apply. We will create a policy so only the user that create the post is able to update and delete their own post. They will not be able to update or delete other people’s post.
Firstly, create a policy :
1 |
php artisan make:policy --model=PostPolicy |
Modify the policy to include update and delete method :
App\Policies\PostPolicy.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
<?php namespace App\Policies; use App\Post; use App\User; use Illuminate\Auth\Access\HandlesAuthorization; class PostPolicy { use HandlesAuthorization; public function update(User $user, Post $post) { return $user->id == $post->author_id; } public function delete(User $user, Post $post) { return $user->id == $post->author_id; } } |
AuthServiceProvider.php
1 2 3 4 5 6 7 8 |
use App\Policies\PostPolicy; use App\Post; ----- protected $policies = [ Post::class => PostPolicy::class, ]; |
Kernel.php
Add the following lines if you find session error :
1 2 3 |
\Laravel\Passport\Http\Middleware\CreateFreshApiToken::class, \Illuminate\Session\Middleware\StartSession::class, \Illuminate\View\Middleware\ShareErrorsFromSession::class, |
Next, open Api/PostController.php and modify update and destroy method :
1 2 3 4 5 6 7 8 9 10 11 |
public function update(PostStoreRequest $request, Post $post) { $this->authorize("update", $post); $post->update($request->only('title', 'slug', 'body')); return response()->json([ 'message' => "Your question has been updated.", 'post' => $post, ]); } |
1 2 3 4 5 6 7 8 9 10 |
public function destroy(Post $post) { $this->authorize("delete", $post); $post->delete(); return response()->json([ 'message' => "Your question has been deleted.", ]); } |
Open Postman and run the PUT and DELETE method :
Api Endpoint to GET Post Detail
In this section, we will create a response when there is post_detail table. Here is the table structure and content :
Create the PostDetail.php model with relation to Post 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 PostDetail extends Model { protected $table = 'post_detail'; public function post() { return $this->belongsTo(Post::class, 'post_id', 'id'); } } |
Subsequently, create a new route with no middleware so anyone will be able to see :
1 |
Route::get('/post/{slug}', 'Api\PostDetailController'); |
Create PostDetailResource :
1 |
php artisan make:resource PostDetailResource |
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 |
<?php namespace App\Http\Resources; use Illuminate\Http\Resources\Json\JsonResource; class PostDetailResource extends JsonResource { /** * Transform the resource into an array. * * @param \Illuminate\Http\Request $request * @return array */ public function toArray($request) { return [ 'id' => $this->id, 'subtitle' => $this->subtitle, 'description' => $this->description, 'slug' => $this->slug, 'view_count' => $this->view_count, 'post' => new PostResource($this->post), ]; } } |
Create PostDetailController inside Api folder :
1 |
php artisan make:controller Api/PostDetailController -i --model=PostDetail |
-i means invoked.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
<?php namespace App\Http\Controllers\Api; use App\Http\Controllers\Controller; use App\Http\Resources\PostDetailResource; use App\Post; use App\PostDetail; class PostDetailController extends Controller { public function __invoke(Post $post, $slug) { $post = PostDetail::where('slug', $slug)->first(); $post->increment('view_count'); return new PostDetailResource($post); } } |
Open Postman and send the request :
We can see that the post detail belongs to another post, and the post is created by user with id number 2.
I have an error when try to run this command:
php artisan route:list --name=passport
The erros is:
Hendis-MacBook-Pro:laravel-api2 hendisantika$ php artisan route:list --name=passport
Error
Class 'App\Providers\Passport' not found
at app/Providers/AuthServiceProvider.php:27
23| */
24| public function boot()
25| {
26| $this->registerPolicies();
> 27| Passport::routes();
28| }
29| }
30|
+8 vendor frames
9 [internal]:0
Illuminate\Foundation\Application::Illuminate\Foundation\{closure}(Object(App\Providers\AuthServiceProvider))
+5 vendor frames
15 artisan:37
Illuminate\Foundation\Console\Kernel::handle(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
Any advices?
Thanks
Please install passport beforehand.
In your AuthServiceProvider.php
add to -> use Laravel\Passport\Passport;
I this tutorial is not details on explaining each step.
Can I help you?