Home » Laravel » How to Create REST API in Laravel

How to Create REST API in Laravel

Modern websites are very often divided into the backend part written in PHP, and the frontend part, developed using one of the popular JavaScript frameworks, for example Vue or React. Backend must only provide API which frontend will use to get all required data.

Backend developers often use the REST approach to create their APIs. This standard was introduced in 1994 but it is still very popular. In this article we will explain what is REST and how to create an API in Laravel.

What Is REST?

REST, or REpresentational State Transfer is a standard that allows web services to communicate with each other easier and in a more structured way. I will not explain all features of the standard in this article. Here are main ideas of REST that we will use:

  • Stateless – all REST requests must not have a state, they must be independent each other. The server shouldn’t store any states between requests. The server and client must understand a request and response without remembering previous responses.
  • Use HTTP verbs – It should be clear what action the server should perform from the HTTP request. You should use the HTTP verb “GET” to get data (but not modify), “POST” to create resources, “PUT” – to modify resources and “DELETE” to delete resources.
  • Manage resources, not commands – you shouldn’t think about API endpoints as commands. Think about them as actions to resources. Each endpoint must perform an action on a specific resource. The resource name must be present in the request path. For example: GET: /articles, DELETE /article/1, etc.
  • Return status code – you should return the correct status code with your response. Return 200 when the request is successful, 201 when resource is created, 204 when the response is empty, for example the resource has been deleted, 403 for permissions issues, 404 when the resource was not found, and 500 for other errors.

Laravel can handle all of this. Next, we will show how to get this done correctly.

How to Create API Using Laravel

In this article we will create a simple RESTful API for articles. The API will allow you to list articles, create, update and delete the article. We will show all Laravel features using this example API. First, let’s create an Article model:

php artisan make:model Article

Then, create the migration file for this model. Let’s imagine, that our articles will contain only title, text, and author fields:

php artisan make:migration CreateArticlesTable

Also, there is no authorization and users. Paste this code to up() method of migration file:

Schema::create("articles", function (Blueprint $table) { $table->id(); $table->string("title"); $table->text("content"); $table->string("author"); $table->timestamps(); });

Then migrate the file. Before we start describing controllers, let’s look at the application structure.

1. Application Structure

Perhaps, you want to place all code in the controller class, for example request validation, business logic and preparing a response. But it is the wrong idea. Controller methods must be as short as possible. You can place all request validation code in a class extended from the FormRequest class. It allows reusing the validation in other similar controllers. Laravel has a command that enables creation of the request class. For example:

php artisan make:request TestRequest

The new class will be placed to the app/Http/Requests directory. You can add all your validation rules to the rules() method. Also it has an authorize() method, where you can restrict access to this request. Likewise you can transform a validated array to a data transfer object and return it to the controller.

All business logic must be added to the Service or Action class. Developers from Spatie recommend using a dedicated Action class for each action that you want to perform. But I prefer to group action methods related to the same functionality into one service class.

You can return a response directly from the controller only if it is empty and has only a status code or contains only a short message. In other cases, you should create a Resource class and prepare the response there. It allows reusing responses and keeps the controller clear. Laravel has a command for creating resources:

php artisan make:resource TestResource

Resource class will appear in the app/Http/Resources directory. The resource expects an Eloquent model or other data as a parameter in construct and has a toArray() method that helps convert this resource into an array. Let’s see all of this in practice.

2. Exceptions Handling

Before we start, let’s look at exception handling in API Laravel. By default the exception and its message will be returned in json only if the client sends the Accept: application/json header. Otherwise the application will redirect you to the home page and you will not know where the problem is.

You can fix it and configure Laravel to always return JSON content for API routes. It is pretty simple. Just open an app/Exceptions/Handler.php file and override shouldReturnJson() method from parent class with this content:

protected function shouldReturnJson($request, Throwable $e): bool { return parent::shouldReturnJson($request, $e) || $request->is("api/*"); }

After this, all exceptions for API routes will be returned as JSON.

1. Create Request

The create request makes a new article model with a title, content and author that the client passes in the request body. You can place all action methods in one controller or make separate controllers for each endpoint with __invoke() method. In this case you don’t need to specify the action method name during route registration. We will use this approach in the article.

First, create the ArticleRequest class:

php artisan make:request ArticleRequest

Change the authorize() method. In the example we don’t use any authorization, so you can simply return true from it:

public function authorize(): bool { return true; }

Next, change the rules() method. Here, add validation rules for the following fields: title, content and author. They must be present in the request and must be a string with a limited length:

public function rules(): array { return [ "title" => ["required", "string", "max:1024"], "content" => ["required", "string", "max:9999"], "author" => ["required", "string", "max:255"], ]; }

You can get an array with validated data in the controller but it will be more convenient when you parse this array in the Request class and return to the controller only data transfer object. Create the data transfer object ArticleData with these fields:

namespace App\DataTransfers; class ArticleData { public string $title; public string $content; public string $author; }

After this, add the getArticleData() method to the ArticleRequest class and move data from the array with validated data to the AtricleData object:

public function getArticleData(): ArticleData { $data = new ArticleData(); $validated = $this->validated(); $data->title = $validated["title"]; $data->content = $validated["content"]; $data->author = $validated["author"]; return $data; }

Next, let’s create the ArticleResource class using the command shown below:

php artisan make:resource ArticleResource

We will send the Article model to this resource. By default JsonResource will display all model fields. But in many cases, it is not a wanted behavior. You can redeclare the toArray() method and determine what fields will be displayed:

public function toArray($request) { return [ "id" => $this->resource->id, "title" => $this->resource->title, "content" => $this->resource->content, "created_at" => $this->resource->created_at, ]; }

As I wrote above, you should not place business logic in the controller. You can create a Service class that will contain all logic. For this example, let’s create ArticleService and add the createArticle() method:

namespace App\Services; use App\DataTransfers\ArticleData; use App\Models\Article; class ArticleService { public function createArticle(ArticleData $data) { $article = new Article(); $article->title = $data->title; $article->content = $data->content; $article->author = $data->author; $article->saveOrFail(); return $article; } }

The method expects an ArticleData object as a parameter and returns a newly created model. Service can hide other abstraction levels, for example repositories, or cache interfaces.

After this, you can make the CreateArticleController class using this command:

php artisan make:controller CreateArticleController

Here, the add __invoke() method with this content:

public function __invoke(ArticleRequest $request, ArticleService $service) { return new ArticleResource( $service->createArticle($request->getArticleData()) ); }

As you see, in the controller we only call the method from the service and pass the Article object to the resource. Next, register the controller in the routes/api.php file:

Route::post( "/articles", \App\Http\Controllers\CreateArticleController::class )->name("articles.create");

Now, your first API request is ready for testing. Run the application server, if you have not done it before:

php artisan serve

The server will listen for connections on port 8080. You can call the request using Postman:

Everything works as expected. The new article was created and returned in the response. Look at the status code in the left bottom corner of the window, it will read 201 Created.

2. Index Request

The index request will be simplier than the previous one. First, add the ArticleService method that will return paginated articles. Laravel supports pagination out of the box, so you can just use the paginate() method from Eloquent query builder.

public function getArticles(): LengthAwarePaginator { return Article::query()->paginate(10); }

You don’t need a separate Request class here because Laravel handles validation of pagination parameters by itself. Also you don’t need the resource class. You can use ArticleResource. Of course, you can make a special resource that expects a collection as the __construct() parameter and transform it into an array. You can do it using the following command:

php artisan make:resource IndexArticleResource --collection

But it is not required. You can use the collection() static method from JsonResource that will do all the work for you. Let’s create IndexArticleController:

php artisan make:controller IndexArticleController

Paste this code into the controller:

class IndexArticleController extends Controller { public function __invoke(ArticleService $service) { return ArticleResource::collection($service->getArticles()); } }

That’s it, now you should register the route in routes/api.php:

Route::get( "/articles", \App\Http\Controllers\IndexArticleController::class )->name("articles.index");

After this, test the request in the Postman:

You can see a list of articles and pagination data.

3. Update Request

In the update request, you can use the same ArticleRequest and ArticleResource classes. But you need to add a few methods to the service class. Add the updateArticle() method to the ArticleService that will update a selected article:

public function updateArticle(Article $article, ArticleData $data): Article { $article->title = $data->title; $article->content = $data->content; $article->author = $data->author; $article->save(); return $article; }

Next, add the findArticleById() method, that will allow finding the Article model by its identifier:

public function findArticleById(string $id): Article { $article = Article::query() ->where("id", $id) ->first(); if ($article === null) { throw new NotFoundHttpException(); } return $article; }

Then create UpdateArticleController:

php artisan make:controller UpdateArticleController

And add __invoke() method with this code:

public function __invoke(string $id, ArticleRequest $request, ArticleService $service) { $article = $service->findArticleById($id); return new ArticleResource( $service->updateArticle($article, $request->getArticleData()) ); }

And finally, register the update route. The route must contain the resource name (articles), and identifier of the resource that you want to modify, and must use the HTTP method PUT:

Route::put('/articles/{id}', \App\Http\Controllers\UpdateArticleController::class) ->name('articles.update');

Now, you can check this request using Postman:

4. Delete Request

The delete request doesn’t need Request and Resource classes. It expects only an article ID as a path parameter and returns an empty response. Create the deleteArticle() method in the service class:

public function deleteArticle(Article $article): void { $article->delete(); }

Perhaps, in this example, you can call $article->delete() directly from the controller, but large projects there can have a complicated logic, for example, deleting relations, etc. So, it will be better if you place it into the service class. Next, create DeleteArticleController:

php artisan make:controller DeleteArticleController

The new controller will appear in the app/Http/Controllers/ directory. Add the __invoke() method to the created class:

public function __invoke(string $id, ArticleService $service) { $article = $service->findArticleById($id); $service->deleteArticle($article); return response()->noContent(204); }

Then, register the route in the routes/api.php file:

Route::delete( "/articles/{id}", \App\Http\Controllers\DeleteArticleController::class )->name("articles.delete");

Now, check the request using Postman. You can see an empty response and the 204 status code:

Wrapping Up

In this article we have explained how to create a REST API in Laravel that allows creating, editing, listing and deleting articles (CRUD). At this step, this API is not secured. You can secure APIs using laravel/passport or sanctum. You can read more about it in the article How to Create Login API in Laravel. What do you think about this application structure? Do you use something like this or everything else? Tell us using the comment form below.

Your Reaction

1 thought on “How to Create REST API in Laravel”

Leave a Comment