APIs and their integration with Laravel

25 Sep 2026 | Peter Cornelis

Modern applications often need to communicate with mobile applications or other services. This is where APIs come in. Today, we are going to dig deeper into APIs and look at how they can be integrated with Laravel.

But before we go into Laravel, let’s first review REST APIs or Representational State Transfer APIs, and how they allow different applications to communicate with each other.

What is a REST API?

A REST API or Representational State Transfer API is an architectural style for creating web services and allows different applications to exchange data with each other.

REST APIs typically use standard HTTP methods to perform different operations on resources:

  1. GET – Retrieve data
  2. POST – Create new data
  3. PUT – Replace or update existing data
  4. PATCH – Partially update existing data
  5. DELETE – Remove data

REST APIs are generally stateless, meaning each request contains the information needed to process it. This allows different systems to communicate regardless of their underlying technology.

Integration in Laravel

Laravel provides two packages for handling your API authentication: Laravel Sanctum and Laravel Passport. The main difference between these two is the way authentication is handled, but more on that later. For our examples, we will be using Laravel Sanctum throughout the post.

It can easily be installed using the following command:

php artisan install:api

This gives your application the tools needed to authenticate API requests and protect routes using Sanctum's authentication middleware.

Routes and Middleware

When you installed Sanctum, Laravel automatically created a routes/api.php file where we can define our API routes.

Route::get('/user', function (Request $request) {
    return $request->user();
})->middleware('auth:sanctum');

We now also have our first API endpoint. Before the request is passed to your application, it passes through middleware. Small, single purpose filters that can inspect, modify or reject requests.

By default Laravel maintains two separate middleware groups:

  1. web – Applied to routes in routes/web.php. Includes session handling, CSRF protection, cookie encryption and so on.
  2. api – Applied to routes in routes/api.php. Skips sessions and CSRF by default, keeping things stateless. When authentication requires a stateful approach this can be enabled in bootstrap/app.php for the api group.

Any custom built middleware can be appended to one of these groups using the withMiddleware method in the bootstrap/app.php file.

The desired middleware can then be added to your route or route group, as usual.

In the above example, the Sanctum authentication middleware was applied to the route.

API Responses

Typically REST APIs use the JSON format. As seen above, for a basic exchange Laravel’s default JsonResponse does the trick.

Our index method in our controller would look something like this:

public function index(): JsonResponse
{
 return response()->json(Movie::all());
}

When you need more control over the response shape, a custom Resource can be made using the following Artisan command:

php artisan make:resource MovieResource

In the created resource, you can then alter the returned data to your needs.

class MovieResource extends JsonResource
{
    public function toArray(Request $request): array
    {
        return [
            'id' => $this->id,
            'title' => $this->title,
            'body' => $this->body,
            'created_at' => $this->created_at->toDateTimeString(),
            'updated_at' => $this->updated_at->toDateTimeString(),
        ];
    }
}

The controller then would look something like this:

class MovieController extends Controller
{
    public function index()
    {
        return MovieResource::collection(Movie::all());
    }
    public function show(int $id)
    {
        return new MovieResource(Movie::findOrFail($id));
    }
}

Pagination

Returning Movie::all() works fine for a handful of records. However, in a production environment you would rarely do this. Here’s where pagination comes in. Pagination splits the results into numbered pages, allowing you to send the results page by page.

We can implement this in the following way:

public function index()
{
    return MovieResource::collection(
        Movie::orderBy('created_at', 'desc')->paginate(20)
    );
}

The response would then return a data array, with the movies for the page, and metadata like current_page, last_page, first_page_url, links (with URLs for the next and previous pages), etc.

Forcing a JSON Response

Often Laravel returns a redirect or HTML response on API routes. For example, if the response is an error. For this case you can easily force a JSON response on all API routes by adding a custom middleware.

namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;

final class ForceJsonResponse
{
public function handle(Request $request, Closure $next): Response
{
$request->headers->set('Accept', 'application/json');
return $next($request);
}
}

After that, just register the created middleware to your bootstrap/app.php:

->withMiddleware(function (Middleware $middleware): void {
$middleware->api(prepend: [
ForceJsonResponse::class,
]);
})->withExceptions(function (Exceptions $exceptions) {
$exceptions->shouldRenderJsonWhen(
fn ($request) => $request->is('api/*')
);
})

By using prepend, this middleware will be run before all the other middleware in the group. Now all Accept headers are changed to JSON responses. Behind the scenes, Laravel does the rest. In the rare case an exception should bypass the middleware, our exception handler catches it.

Authentication

Now that we discussed the basics of REST APIs and their data handling, we can take a closer look at authentication.

Three common ways of authenticating with APIs:

  1. Session based authentication - Using this method the server stores a session ID and the browser sends it back through a cookie on each request. This is the only non-stateless method we are going to discuss. It works well with web apps that are hosted on the same domain as the backend.
  2. Token based authentication - In this stateless method authentication gets done by verifying the tokens that are delivered with every request.
  3. OAuth2 based authentication – With this approach, a separate authorization server authenticates the user and issues short lived access tokens. The resource server then validates them without knowing the user’s password.

In Laravel the Sanctum package can handle session and token based authentication, while Passport can handle OAuth2 authentication. In general, Sanctum does the trick, but if you don’t control both sides of the API connection and OAuth2 authentication is required then Laravel Passport is the go-to option.

Timeouts and failure handling

When working with APIs, failures are inevitable. An external API might be unavailable, take too long to respond, or return an error. Laravel’s HTTP client can handle these failures gracefully:

$response = Http::timeout(5)->get('https://example.com/api/movies');

In the above example the application will wait 5 seconds for a response, if no response was received after that a timeout exception will be thrown.

It’s also possible to use retries for temporary failures:

$response = Http::retry(3, 100)-> get('https://example.com/api/movies');

This tells the application to retry up to 3 times, leaving 100ms between attempts.

Laravel also provides a centralized way to customize exception handling in bootstrap/app.php. For example, we can customize how validation errors are returned:

->withExceptions(function ($exceptions) {
$exceptions->renderable(function (ValidationException $e, Request $r) {
        return response()->json([
            'success' => false,
            'error' => [
                'code' => 'VALIDATION_ERROR',
'message' => $e->getMessage(),
'status' => 422,
            ]
        ], 422);
    });
});

This keeps the API’s error format consistent instead of having to manually construct the same response throughout the application.

Security and Rate Limiting

Laravel Sanctum and Passport also provide authorization scopes, by which you can determine whether a user is allowed to perform a certain action.

$user->createToken('token-name', ['movies:read']);

In the above example we created a token, which only has read access to our movies.

Other options could be movies:update, movies:create or movies:delete.

This can then later be checked using tokenCan('movies:read') method.

For rate limiting we can use the familiar throttle middleware. By default  60 requests per minute are allowed. This limit can be reduced to protect against abuse, minimize server load and secure your endpoints.

For example:

Route::middleware(['auth:sanctum', 'throttle:30,1'])->group(function () {
Route::get('/movies', [MovieController::class, 'index']);
});

Conclusion

APIs are an important part of application development, and there is far more to learn then I can sammurize in this post. However, in my experience, Sanctum and Passport make working with APIs a elegant and manageable experience.

Peter Cornelis

Peter Cornelis

Software Engineer