When building a public-facing API, one of the most important considerations is preventing abuse. If an API is completely open without any restrictions, a single malicious user or a runaway script could flood your server with requests, causing it to crash or run up massive infrastructure bills.
In this tutorial, we will build a public API from scratch using Laravel and secure it against abuse by implementing rate limiting using Laravel's powerful built-in rate limiter.
Step 1: Setting Up the Laravel Project
Let's start by creating a new Laravel project. Open your terminal and run:
composer create-project laravel/laravel rate-limited-api
cd rate-limited-api
Next, let's create a simple model and migration for the data we want to expose through our API. For this example, let's assume we are building an API that serves a list of books.
php artisan make:model Book -m
In the generated migration file, define the schema for the books table:
public function up()
{
Schema::create('books', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->string('author');
$table->text('description');
$table->timestamps();
});
}
Run the migration to create the table:
php artisan migrate
Step 2: Creating the API Controller
Next, let's create a controller to handle the API requests.
php artisan make:controller Api/BookController
Open app/Http/Controllers/Api/BookController.php and add a method to return the list of books:
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Models\Book;
use Illuminate\Http\Request;
class BookController extends Controller
{
public function index()
{
$books = Book::all();
return response()->json([
'status' => 'success',
'data' => $books
]);
}
}
Step 3: Defining the API Route
Now we need to define the route for our API endpoint. Open routes/api.php and add the following:
use App\Http\Controllers\Api\BookController;
use Illuminate\Support\Facades\Route;
Route::get('/books', [BookController::class, 'index']);
Step 4: Implementing Rate Limiting
By default, Laravel includes a api middleware group in app/Http/Kernel.php which automatically applies a basic rate limit (usually 60 requests per minute). But let's define a custom, stricter rate limit for our public API.
Open app/Providers/RouteServiceProvider.php. In the configureRateLimiting method, you can define your custom rate limiter. Let's create a rate limiter called public-api that restricts users to 10 requests per minute based on their IP address:
use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Support\Facades\RateLimiter;
protected function configureRateLimiting()
{
RateLimiter::for('public-api', function (Request $request) {
return Limit::perMinute(10)->by($request->ip());
});
}
Now, apply this rate limiter to our API route in routes/api.php by attaching the throttle middleware:
Route::middleware('throttle:public-api')->group(function () {
Route::get('/books', [BookController::class, 'index']);
});
Step 5: Testing the Rate Limit
You can test the rate limit using tools like Postman, Insomnia, or simply via cURL in your terminal.
Make a GET request to /api/books. Notice the headers in the response:
X-RateLimit-Limit: 10X-RateLimit-Remaining: 9
If you make more than 10 requests within a minute, Laravel will automatically return a 429 Too Many Requests HTTP response, preventing further access until the time window resets.
Conclusion
Rate limiting is a fundamental aspect of building robust public APIs. Laravel's built-in rate limiter makes it incredibly easy to define and apply complex rate limiting strategies with just a few lines of code, protecting your application from abuse and ensuring fair usage for all users.