As developers, we're always striving for that sweet spot: clean, efficient code that performs exceptionally well. If you're building Laravel applications, especially APIs handling significant traffic, performance optimization isn't just a "nice-to-have" – it's essential.
I learned this the hard way while scaling Laravel APIs. The bottleneck? Redundant database queries. Every authenticated request was hitting the database not once, but twice! First, to verify the user's identity, and then again for Laravel Sanctum to validate the personal access token. Imagine this multiplied by thousands of requests – your database ends up working overtime, slowing everything down.
To tackle this head-on, I developed two solutions focused on caching. These techniques drastically reduce unnecessary database queries by caching user data and Sanctum personal access tokens. The result? Faster response times and a much happier database!
The Performance Drain
In a typical Laravel API setup, authentication can become a hidden performance drain. Here's why:
- User Data Retrieval: For each authenticated API request, Laravel fetches user data from your database to confirm the user's identity.
- Sanctum Token Verification: If you're using Laravel Sanctum for API token authentication (a best practice!), another database query is executed to validate the provided personal access token.
That's two database queries for every single authenticated request! In high-traffic scenarios, these repetitive queries become a major performance bottleneck, impacting API speed and scalability.
The Solution: Caching!
The answer to this problem is straightforward: caching. By strategically caching user authentication data and Sanctum tokens, we can bypass these repetitive database lookups. This leads to:
- Faster API Responses: Requests are processed much quicker as data is retrieved from the cache instead of the database.
- Reduced Database Load: Fewer queries mean less strain on your database server, improving overall application stability and responsiveness.
- Improved Scalability: Your API can handle more concurrent requests without performance degradation.
Let's dive into how to implement these caching techniques in your Laravel application.
1. Caching User Data with CachedUserProvider
The first step is to cache user data at the authentication level. I created a custom authentication provider, CachedUserProvider
, to achieve this. This provider intercepts the default Laravel authentication process and introduces caching.
How It Works:
The CachedUserProvider
works by:
- Attempting to retrieve user data from the cache first. If the user data is already in the cache (identified by a unique key, like
user_
+ user ID), it's served directly from the cache, skipping the database query. - Querying the database only if the user data is not in the cache. After fetching the data from the database, it's stored in the cache for subsequent requests.
Implementation:
-
Create
CachedUserProvider.php
: Add this file to your project, for example, underapp/Providers/Auth/
. You can find the code forCachedUserProvider
in this repository: .Laravel-Cache-User-To-Avoid-Extra-Query-Each-Request on GitHub -
Register the Provider: In your
bootstrap/providers.php
(orconfig/app.php
for older Laravel versions), register theCachedUserProvider
. -
Override the Auth Provider in
AuthServiceProvider
: In yourapp/Providers/AuthServiceProvider.php
, modify theboot
method to override the defaultauth
provider:PHP<?php namespace App\Providers; use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider; use Illuminate\Support\Facades\Auth; use App\Providers\Auth\CachedUserProvider; // Assuming you placed it here class AuthServiceProvider extends ServiceProvider { // ... public function boot() { $this->registerPolicies(); Auth::provider('cached', function ($app, array $config) { return new CachedUserProvider($app['hash'], $config['model']); }); } }
Keeping Cached User Data Fresh
To ensure data accuracy, the cache needs to be updated whenever user information changes. A UserObserver
is an elegant way to handle this:
-
Create
UserObserver.php
: Create an observer to handle user model events, like updates.PHP<?php namespace App\Observers; use App\Models\User; // Or your User model namespace use Illuminate\Support\Facades\Cache; class UserObserver { public function updated(User $user) { Cache::forget('user_' . $user->id); // Invalidate old cache Cache::put('user_' . $user->id, $user); // Re-cache updated user } public function created(User $user) { Cache::put('user_' . $user->id, $user); // Cache new user } public function deleted(User $user) { Cache::forget('user_' . $user->id); // Clear cache on delete } }
-
Register the Observer: In your
AppServiceProvider.php
(or a dedicated observer service provider), register theUserObserver
with yourUser
model:PHP<?php namespace App\Providers; use App\Models\User; use App\Observers\UserObserver; use Illuminate\Support\ServiceProvider; class AppServiceProvider extends ServiceProvider { // ... public function boot() { User::observe(UserObserver::class); } }
Now, whenever user data is updated, created, or deleted, the cache will be automatically updated, ensuring data freshness while minimizing database queries!
2. Caching Sanctum Personal Access Tokens
The second optimization focuses on caching Laravel Sanctum personal access tokens. Without caching, each API request triggers a database query to the personal_access_tokens
table to verify token validity.
To eliminate these redundant queries, we can cache the tokens. The Cache-Personal-Access-Tokens-In-Laravel
repository provides a solution for this:
How to Use It:
-
Create
PersonalAccessToken.php
: Add this file to yourModels
folder. You can find the code forPersonalAccessToken
in the linked repository above. This custom model extends Sanctum's default token model and incorporates caching logic. -
Register in Service Provider: In your
AppServiceProvider.php
(or another relevant service provider), tell Sanctum to use your customPersonalAccessToken
model:PHP<?php namespace App\Providers; use App\Models\PersonalAccessToken; // Your custom model use Illuminate\Support\ServiceProvider; use Laravel\Sanctum\Sanctum; class AppServiceProvider extends ServiceProvider { // ... public function boot() { Sanctum::usePersonalAccessTokenModel(PersonalAccessToken::class); } }
Invalidating Cached Tokens
Just like user data, cached tokens need to be invalidated when access is revoked (e.g., user logout, token revocation). The PersonalAccessToken
model in the linked repository typically includes logic to handle token invalidation and cache clearing when tokens are revoked or deleted.
You'll generally want to ensure that when you revoke a token (using Sanctum's token management features), the corresponding cache entry is also removed. Refer to the Cache-Personal-Access-Tokens-In-Laravel
repository for specific implementation details on token invalidation.
Conclusion
Implementing these two caching techniques can dramatically reduce database queries in your Laravel applications, leading to significant performance improvements and enhanced scalability. If you're running a high-traffic API, or simply want to optimize your Laravel application for speed, I highly recommend incorporating these caching strategies.
By caching user data and Sanctum personal access tokens, you'll not only make your API faster but also reduce the load on your database, ensuring a more robust and responsive application for your users. Start implementing these optimizations today and experience the difference!
0 comments:
Post a Comment