API Design Foundations
When you start an API project, the first decisions you make affect every line of code that follows. A well‑structured contract reduces friction between front‑ends, mobile apps, and third‑party consumers.
- Resource‑oriented URLs – Keep endpoints noun‑based and hierarchical. Example:
GET /api/v1/ordersreturns a collection,GET /api/v1/orders/123returns a single order. - Versioning – Prefix the path (
/api/v1/…) or use a request header (Accept: application/vnd.myapp.v1+json). Versioning lets you evolve without breaking existing clients. - Statelessness – Every request must contain everything the server needs to process it. Avoid server‑side sessions for API calls; rely on tokens instead.
- Pagination, Filtering, Sorting – Provide query parameters like
?page=2&per_page=25&sort=-created_at&status=paid. Laravel’spaginate()and query scopes make this trivial.
// Example controller method
public function index(Request $request)
{
$orders = Order::query()
->when($request->filled('status'), fn($q) => $q->where('status', $request->status))
->orderBy($request->get('sort', 'created_at'))
->paginate($request->get('per_page', 20));
return OrderResource::collection($orders);
}
Authentication & Authorization
Token‑Based Authentication
Laravel Sanctum and Laravel Passport are the two mainstream choices. For most SaaS startups, Sanctum offers a lightweight, cookie‑less solution that works well with SPAs and mobile apps.
composer require laravel/sanctum
php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"
php artisan migrate
Add the middleware to api group:
// app/Http/Kernel.php
'api' => [
\Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
'throttle:api',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
Clients receive a plain‑text token (Personal Access Token) that they send in the Authorization: Bearer <token> header. Tokens are stored hashed, so a stolen DB dump does not expose credentials.
Role‑Based Access Control (RBAC)
Laravel’s built‑in Gates and Policies let you separate who can do what from the controller logic.
// app/Policies/OrderPolicy.php
public function view(User $user, Order $order)
{
return $user->id === $order->user_id || $user->hasRole('admin');
}
Register the policy in AuthServiceProvider and call authorize() in the controller. This keeps authorization declarative and testable.
Input Validation & Sanitization
Never trust client data. Laravel’s request validation is expressive and runs before your business logic.
// app/Http/Requests/StoreOrderRequest.php
public function rules()
{
return [
'product_id' => 'required|exists:products,id',
'quantity' => 'required|integer|min:1',
'address' => 'required|string|max:255',
];
}
If you need to guard against mass assignment, define $fillable on the model or use $request->only([...]).
Sanitizing Output
When returning data that will be rendered in a browser (e.g., HTML snippets), escape it. Laravel’s API resources automatically cast values, but any raw HTML should be sanitized with a library like HTMLPurifier.
Consistent Responses & Error Handling
Clients thrive on predictability. Adopt a uniform envelope for all responses.
{
"data": { … },
"meta": { "pagination": { … } },
"error": null
}
Laravel’s Resource classes already wrap data under a data key. For errors, create a helper that returns a JSON payload with an HTTP status code.
// app/Helpers/ApiResponse.php
function api_error(string $message, int $code = 400, array $details = [])
{
return response()->json([
'data' => null,
'error' => [
'message' => $message,
'code' => $code,
'details' => $details,
],
], $code);
}
In app/Exceptions/Handler.php, transform uncaught exceptions into this format. This way front‑ends never have to parse HTML error pages.
Security Hardening
CSRF & CORS
CSRF is irrelevant for stateless APIs that use tokens, but you must still configure CORS correctly. Laravel’s fruitcake/laravel-cors package (included since Laravel 7) lets you whitelist origins.
// config/cors.php
'paths' => ['api/*'],
'allowed_methods' => ['*'],
'allowed_origins' => ['https://app.mycompany.com'],
'allowed_headers' => ['*'],
'exposed_headers' => [],
'max_age' => 0,
'supports_credentials' => false,
Rate Limiting & Throttling
Laravel’s built‑in throttle middleware (throttle:60,1) limits a route to 60 requests per minute per IP. For token‑based APIs, you can scope limits per user.
// routes/api.php
Route::middleware('auth:sanctum', 'throttle:api')->group(function () {
Route::apiResource('orders', OrderController::class);
});
Define a custom rate limiter in AppServiceProvider if you need more granularity:
RateLimiter::for('api', function (Request $request) {
return Limit::perMinute(120)->by(optional($request->user())->id ?: $request->ip());
});
Input Sanitization & SQL Injection
Laravel’s query builder and Eloquent use prepared statements automatically, eliminating classic SQL injection vectors. Still, avoid raw queries unless you bind parameters explicitly.
// Safe
DB::select('SELECT * FROM orders WHERE status = ?', [$status]);
HTTPS Everywhere
Terminate TLS at the load balancer (AWS ALB, Cloudflare, etc.) and enforce Strict-Transport-Security headers. In Laravel, set APP_URL=https://api.myapp.com and enable
Deployment Checklist for Production APIs
- Environment variables – Keep secrets out of source control. Use Laravel’s
config:cacheto load them efficiently. - Database migrations – Run them in a zero‑downtime manner (use
php artisan migrate --step). - Cache configuration – Store frequently accessed data (e.g., product catalogs) in Redis; set appropriate TTLs.
- Queue workers – Offload long‑running tasks (email, PDF generation) to Laravel Horizon or Supervisor‑managed workers.
- Health checks – Expose a lightweight
/healthendpoint that returns200only when DB, cache, and queue connections are healthy. - Logging – Use
logstash‑compatible JSON logs (LOG_CHANNEL=stack) and ship them to a centralized service (Papertrail, ELK). Never log raw tokens. - Monitoring – Set up latency alerts on critical endpoints (e.g., order creation). Tools like Laravel Telescope or third‑party APMs give insight without code changes.
- Backup & Disaster Recovery – Automate daily snapshots of databases and object storage; test restore procedures quarterly.
By following these guidelines, you’ll deliver an API that is easy to consume, hard to compromise, and ready to scale as your user base grows.
Takeaway: Start with a solid contract, secure every request with token‑based auth, validate aggressively, and enforce consistent responses. Then lock down the runtime environment with CORS, rate limiting, and proper monitoring. The result is an API that lets your front‑end teams move fast while keeping the security and reliability that investors and customers expect.
- Laravel
- Api
- Security
- Php
- Rest