Back to Blog
    Laravel 13 API developmentLaravel 13 REST APILaravel API authenticationLaravel SanctumLaravel API ResourcesLaravel API validation

    Laravel 13 API Development: Build Fast & Secure REST APIs

    Learn Laravel 13 API development with RESTful routes, controllers, API Resources, Sanctum authentication, validation, rate limiting, pagination, testing, and security best practices.

    2026-09-11 · Abhilash Raj · 16 min read

    Laravel 13 API Development: Build Fast & Secure REST APIs

    Laravel 13 API Development: Build Fast & Secure REST APIs

    Modern applications rarely work as a single piece of software.

    A Laravel backend may need to communicate with a React or Vue frontend, mobile applications, desktop software, third-party integrations, JavaScript clients, or other backend services.

    That is where APIs become essential.

    Laravel 13 provides a clean and powerful foundation for building REST APIs, including routing, controllers, validation, API Resources, authentication, rate limiting, pagination, database integration, and automated testing.

    Laravel 13 can be used as the API backend for JavaScript applications, mobile applications, and other clients, while continuing to provide Laravel's authentication, database, queues, notifications, and other application services.

    In this guide, we'll build a practical Laravel 13 REST API and cover the important concepts you should understand before putting an API into production.


    What Is a REST API?

    A REST API allows different applications to communicate using HTTP.

    For example, a frontend application might request:

    GET /api/products
    

    The Laravel application processes the request and returns JSON:

    {
        "data": [
            {
                "id": 1,
                "name": "Laravel Course",
                "price": 49.99
            }
        ]
    }
    

    A typical REST API uses HTTP methods such as:

    MethodPurpose
    GETRetrieve data
    POSTCreate data
    PUTReplace/update data
    PATCHPartially update data
    DELETEDelete data

    A simple product API might look like:

    GET     /api/products
    GET     /api/products/{id}
    POST    /api/products
    PUT     /api/products/{id}
    DELETE  /api/products/{id}
    

    This structure makes the API predictable for frontend and mobile developers.


    Why Use Laravel 13 for API Development?

    Laravel already provides many of the building blocks required for a production API.

    You get:

    • Routing
    • Controllers
    • Middleware
    • Request validation
    • Eloquent ORM
    • API Resources
    • Authentication
    • Authorization
    • Rate limiting
    • Pagination
    • HTTP testing
    • Error handling
    • Queues
    • Caching
    • Database transactions

    Laravel 13 also adds first-party JSON:API resources, which can be useful when an API needs to follow the JSON:API specification.

    This means you don't need to assemble your API architecture completely from scratch.


    Creating an API in Laravel 13

    If you're starting a new Laravel 13 application, you can install the API routing and authentication setup with:

    php artisan install:api
    

    Laravel's API installation process can install Sanctum and configure API routing for applications that need API functionality.

    After installation, you can work with:

    routes/api.php
    

    A basic API route could look like:

    use Illuminate\Support\Facades\Route;
    
    Route::get('/products', function () {
        return [
            'products' => []
        ];
    });
    

    However, for a real application, you should move business logic into controllers rather than keeping everything inside route closures.


    Creating an API Controller

    You can generate a controller using Artisan:

    php artisan make:controller Api/ProductController
    

    You can then define methods such as:

    namespace App\Http\Controllers\Api;
    
    use App\Http\Controllers\Controller;
    
    class ProductController extends Controller
    {
        public function index()
        {
            // Return products
        }
    
        public function store()
        {
            // Create product
        }
    
        public function show(string $id)
        {
            // Return product
        }
    
        public function update(string $id)
        {
            // Update product
        }
    
        public function destroy(string $id)
        {
            // Delete product
        }
    }
    

    Keeping API controllers organized under an Api namespace makes larger applications easier to maintain.


    API Resource Routes

    For CRUD APIs, Laravel provides resource routing.

    For example:

    use App\Http\Controllers\Api\ProductController;
    
    Route::apiResource(
        'products',
        ProductController::class
    );
    

    This automatically creates the common API routes.

    Conceptually:

    GET       /api/products
    POST      /api/products
    GET       /api/products/{product}
    PUT/PATCH /api/products/{product}
    DELETE    /api/products/{product}
    

    You can inspect the generated routes with:

    php artisan route:list
    

    This is particularly useful when debugging API route configuration.


    API Controllers and Eloquent

    Suppose you have a Product model:

    namespace App\Models;
    
    use Illuminate\Database\Eloquent\Model;
    
    class Product extends Model
    {
        protected $fillable = [
            'name',
            'description',
            'price',
        ];
    }
    

    Your controller can retrieve products using Eloquent:

    use App\Models\Product;
    
    public function index()
    {
        return Product::latest()->get();
    }
    

    Laravel automatically converts the Eloquent collection into a JSON response when returned from an API endpoint.

    However, directly returning Eloquent models isn't always the best approach for production APIs.

    That's where API Resources become useful.


    Laravel API Resources

    API Resources allow you to control exactly how your models are transformed into JSON.

    Create one with:

    php artisan make:resource ProductResource
    

    Then:

    namespace App\Http\Resources;
    
    use Illuminate\Http\Request;
    use Illuminate\Http\Resources\Json\JsonResource;
    
    class ProductResource extends JsonResource
    {
        public function toArray(Request $request): array
        {
            return [
                'id' => $this->id,
                'name' => $this->name,
                'description' => $this->description,
                'price' => $this->price,
            ];
        }
    }
    

    Your controller can now return:

    use App\Http\Resources\ProductResource;
    use App\Models\Product;
    
    public function show(Product $product)
    {
        return new ProductResource($product);
    }
    

    For collections:

    public function index()
    {
        return ProductResource::collection(
            Product::latest()->paginate(20)
        );
    }
    

    This gives you much better control over the public API contract.


    Why API Resources Matter

    Imagine your database contains:

    id
    name
    description
    price
    cost_price
    supplier_id
    internal_notes
    created_at
    updated_at
    

    You probably don't want to expose all of those fields to API consumers.

    A Resource allows you to expose only what the client needs:

    {
        "data": {
            "id": 10,
            "name": "Laravel Course",
            "description": "Learn Laravel",
            "price": 49.99
        }
    }
    

    This provides:

    • Cleaner responses
    • Better security
    • Stable API contracts
    • Easier versioning
    • Less unnecessary data

    API Validation

    Never trust data coming from an API client.

    A request might contain:

    {
        "name": "",
        "price": "abc"
    }
    

    Laravel provides powerful request validation.

    For example:

    public function store(Request $request)
    {
        $validated = $request->validate([
            'name' => ['required', 'string', 'max:255'],
            'description' => ['nullable', 'string'],
            'price' => ['required', 'numeric', 'min:0'],
        ]);
    
        $product = Product::create($validated);
    
        return new ProductResource($product);
    }
    

    Laravel automatically handles validation failures and can return appropriate validation error responses for API requests.


    Form Request Validation

    For larger applications, move validation into a Form Request.

    Create one:

    php artisan make:request StoreProductRequest
    

    Then:

    class StoreProductRequest extends FormRequest
    {
        public function authorize(): bool
        {
            return true;
        }
    
        public function rules(): array
        {
            return [
                'name' => ['required', 'string', 'max:255'],
                'description' => ['nullable', 'string'],
                'price' => ['required', 'numeric', 'min:0'],
            ];
        }
    }
    

    Your controller becomes cleaner:

    public function store(StoreProductRequest $request)
    {
        $product = Product::create(
            $request->validated()
        );
    
        return new ProductResource($product);
    }
    

    This is a better structure for production applications.


    Laravel API Authentication with Sanctum

    Most private APIs need authentication.

    Laravel Sanctum is designed for:

    • SPA authentication
    • Mobile applications
    • Simple token-based APIs
    • Personal access tokens

    Laravel's documentation recommends Sanctum for these types of applications.

    After installing API support:

    php artisan install:api
    

    your application can use Sanctum.


    Adding Sanctum to the User Model

    The User model can use the HasApiTokens trait:

    use Laravel\Sanctum\HasApiTokens;
    
    class User extends Authenticatable
    {
        use HasApiTokens;
    }
    

    This allows users to create personal access tokens.

    For example:

    $token = $user->createToken(
        'mobile-app'
    );
    

    The plain-text token is available immediately after creation:

    $token->plainTextToken;
    

    Sanctum stores the token securely and API clients can send it using the Authorization header.


    Using Bearer Tokens

    An API client can send:

    Authorization: Bearer YOUR_API_TOKEN
    

    Laravel Sanctum can then authenticate the request.

    For example:

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

    Now the endpoint requires authentication.


    Protecting API Routes

    You can protect a group of routes:

    Route::middleware('auth:sanctum')->group(function () {
    
        Route::apiResource(
            'products',
            ProductController::class
        );
    
    });
    

    Now users must be authenticated before accessing the product API.


    Token Abilities

    Sanctum tokens can also have abilities.

    For example:

    $token = $user->createToken(
        'admin-client',
        ['products:create', 'products:update']
    );
    

    Your application can then check whether the token has a particular ability.

    This is useful when different API clients require different permissions.

    For example:

    Mobile App
       ↓
    products:view
    
    Admin Dashboard
       ↓
    products:view
    products:create
    products:update
    products:delete
    

    This provides a more granular authorization model.


    Authentication vs Authorization

    These concepts are different.

    Authentication

    Authentication answers:

    Who is this user?

    For example:

    User → Login → Token → Authenticated
    

    Authorization

    Authorization answers:

    What is this user allowed to do?

    For example:

    Admin → Delete Product → Allowed
    
    Customer → Delete Product → Denied
    

    A secure API needs both.


    API Authorization with Policies

    Laravel Policies provide a clean way to authorize actions.

    Create a policy:

    php artisan make:policy ProductPolicy --model=Product
    

    Then you can define:

    public function update(
        User $user,
        Product $product
    ): bool {
        return $user->id === $product->user_id;
    }
    

    Your API controller can then authorize the action before updating the record.

    This prevents users from modifying resources that don't belong to them.


    Route Model Binding

    Laravel makes it easy to retrieve models from route parameters.

    Instead of:

    public function show($id)
    {
        $product = Product::findOrFail($id);
    
        return new ProductResource($product);
    }
    

    you can use:

    public function show(Product $product)
    {
        return new ProductResource($product);
    }
    

    Laravel automatically resolves the model from the route parameter.

    This makes API controllers cleaner.


    Pagination

    Never return thousands of database records in a single API response.

    Instead:

    $products = Product::latest()
        ->paginate(20);
    
    return ProductResource::collection($products);
    

    The API can then return pagination information along with the data.

    A frontend can request:

    /api/products?page=2
    

    This keeps responses smaller and reduces database and network load.


    Filtering API Results

    A useful API should allow clients to filter results.

    For example:

    /api/products?category=software
    

    Your controller might use:

    $query = Product::query();
    
    if ($request->filled('category')) {
        $query->where(
            'category',
            $request->category
        );
    }
    
    $products = $query
        ->latest()
        ->paginate(20);
    
    return ProductResource::collection($products);
    

    Always validate filter parameters rather than blindly trusting arbitrary request input.


    Searching

    You can also support simple search:

    $query = Product::query();
    
    if ($request->filled('search')) {
        $query->where(
            'name',
            'like',
            '%' . $request->search . '%'
        );
    }
    

    For larger applications with sophisticated search requirements, consider Laravel Scout and a dedicated search engine rather than relying entirely on SQL LIKE queries.


    API Rate Limiting

    Public APIs should not allow unlimited requests.

    Without rate limiting, an attacker or badly written client could generate thousands of requests.

    Laravel provides rate-limiting capabilities that can be applied to API routes.

    Conceptually:

    Client
       ↓
    Rate Limiter
       ↓
    Allowed?
      ↙   ↘
    Yes    No
     ↓      ↓
    API    429
    

    A common response to excessive requests is:

    429 Too Many Requests
    

    Rate limiting is especially important for:

    • Login endpoints
    • Password reset
    • Public APIs
    • Search endpoints
    • Expensive operations
    • Payment-related endpoints

    API Error Responses

    Your API should return predictable errors.

    For example:

    {
        "message": "Product not found."
    }
    

    For validation errors:

    {
        "message": "The given data was invalid.",
        "errors": {
            "name": [
                "The name field is required."
            ],
            "price": [
                "The price must be a number."
            ]
        }
    }
    

    Consistent error structures make frontend development much easier.


    HTTP Status Codes

    Use appropriate HTTP status codes.

    StatusMeaning
    200Successful request
    201Resource created
    204Successful request with no content
    400Bad request
    401Unauthenticated
    403Unauthorized
    404Resource not found
    422Validation error
    429Too many requests
    500Server error

    For example, after creating a product:

    return response()->json(
        new ProductResource($product),
        201
    );
    

    Using the correct status code gives API consumers useful information without requiring them to interpret application-specific messages.


    API Versioning

    As your API grows, changing the existing response format can break clients.

    Instead of:

    /api/products
    

    you can use:

    /api/v1/products
    

    Later:

    /api/v2/products
    

    A typical structure might be:

    app/
    └── Http/
        └── Controllers/
            └── Api/
                ├── V1/
                │   └── ProductController.php
                └── V2/
                    └── ProductController.php
    

    Versioning is especially useful when your API has external consumers that you don't control.


    API Response Consistency

    Try to keep your response format predictable.

    For example:

    {
        "data": {
            "id": 1,
            "name": "Product"
        }
    }
    

    For collections:

    {
        "data": [
            {
                "id": 1,
                "name": "Product A"
            },
            {
                "id": 2,
                "name": "Product B"
            }
        ]
    }
    

    Don't randomly change between:

    {
        "product": {}
    }
    

    and:

    {
        "data": {}
    }
    

    unless there is a deliberate reason.

    Consistency is one of the most important characteristics of a good API.


    Avoid Returning Sensitive Database Fields

    Suppose your users table contains:

    id
    name
    email
    password
    remember_token
    api_secret
    

    Never blindly return:

    return User::all();
    

    Instead, use an API Resource:

    return [
        'id' => $this->id,
        'name' => $this->name,
        'email' => $this->email,
    ];
    

    This creates a clear boundary between your internal database structure and your public API.


    Prevent N+1 Queries

    APIs can accidentally introduce N+1 query problems.

    For example:

    $posts = Post::all();
    
    foreach ($posts as $post) {
        echo $post->author->name;
    }
    

    This can execute many additional queries.

    Instead:

    $posts = Post::with('author')->get();
    

    This eager loads the relationship.

    This is particularly important for APIs because a single endpoint can be called thousands of times.

    For more on database performance, see the related guide:

    Laravel 13 Database Optimization: Eloquent, Indexing & Query Performance


    Caching API Responses

    Frequently requested API data can also be cached.

    For example:

    $products = Cache::remember(
        'api.products',
        300,
        fn () => Product::latest()
            ->take(50)
            ->get()
    );
    

    Caching can significantly reduce database load for endpoints that repeatedly return the same or slowly changing information.

    For a deeper look at Laravel caching and Redis, see:

    Laravel 13 Caching: Redis, Cache Drivers & Real-World Examples


    Queues for Expensive API Operations

    Some API operations shouldn't happen during the HTTP request.

    For example:

    • Sending hundreds of emails
    • Generating reports
    • Processing uploaded images
    • Importing large CSV files
    • Calling slow external services

    Instead, dispatch a queue job:

    ProcessReport::dispatch($report);
    

    Then return a response immediately:

    {
        "message": "Report processing started."
    }
    

    This produces a much better user experience.

    For more information, see:

    Laravel 13 Queues & Jobs: A Practical Guide to Background Processing


    API Testing

    APIs should be tested automatically.

    Laravel provides HTTP testing tools that make it easy to send requests to your application's routes.

    For example:

    public function test_products_can_be_listed(): void
    {
        Product::factory()->count(3)->create();
    
        $response = $this->getJson('/api/products');
    
        $response
            ->assertOk()
            ->assertJsonStructure([
                'data'
            ]);
    }
    

    You can test:

    • Authentication
    • Validation
    • CRUD operations
    • Authorization
    • Status codes
    • JSON structure
    • Pagination
    • Rate limiting
    • Error responses

    Testing Authenticated APIs

    Sanctum provides helpers for testing authenticated API requests.

    For example:

    Sanctum::actingAs(
        User::factory()->create(),
        ['products:view']
    );
    
    $response = $this->getJson(
        '/api/products'
    );
    
    $response->assertOk();
    

    Laravel's Sanctum documentation provides Sanctum::actingAs() specifically for authenticating users during tests.


    API Security Best Practices

    A production API should follow several security principles.

    1. Always Use HTTPS

    Never send authentication tokens over plain HTTP.

    Use:

    https://api.example.com
    

    instead of:

    http://api.example.com
    

    2. Validate Every Input

    Don't trust:

    • Query parameters
    • JSON bodies
    • Headers
    • File uploads
    • Route parameters

    Validate anything that affects application behavior.


    3. Use Authentication

    Private resources should require authentication.

    For Sanctum:

    ->middleware('auth:sanctum')
    

    4. Use Authorization

    Authentication alone isn't enough.

    A logged-in user should not automatically have permission to modify every record.

    Use policies and abilities.


    5. Rate Limit Sensitive Endpoints

    Especially:

    /login
    /password/reset
    /search
    /api/*
    

    6. Don't Expose Secrets

    Never return:

    password
    api_secret
    private_token
    database credentials
    internal configuration
    

    7. Avoid Mass Assignment Vulnerabilities

    Use $fillable or $guarded carefully.

    For example:

    protected $fillable = [
        'name',
        'description',
        'price',
    ];
    

    Then use validated input:

    Product::create(
        $request->validated()
    );
    

    A Production Laravel 13 API Architecture

    A scalable API might look like:

    Client
      │
      ↓
    HTTPS
      │
      ↓
    Rate Limiting
      │
      ↓
    Authentication
      │
      ↓
    Authorization
      │
      ↓
    Controller
      │
      ├── Validation
      │
      ├── Service Layer
      │
      ├── Eloquent
      │
      ├── Cache
      │
      └── Queue
      │
      ↓
    API Resource
      │
      ↓
    JSON Response
    

    This separation makes applications easier to maintain as they grow.


    Recommended Laravel 13 API Folder Structure

    For a larger project, you might use:

    app/
    ├── Http/
    │   ├── Controllers/
    │   │   └── Api/
    │   │       └── V1/
    │   │           ├── AuthController.php
    │   │           └── ProductController.php
    │   │
    │   ├── Requests/
    │   │   └── Api/
    │   │       ├── StoreProductRequest.php
    │   │       └── UpdateProductRequest.php
    │   │
    │   └── Resources/
    │       └── Api/
    │           └── V1/
    │               └── ProductResource.php
    │
    ├── Models/
    │   └── Product.php
    │
    ├── Policies/
    │   └── ProductPolicy.php
    │
    ├── Services/
    │   └── ProductService.php
    │
    └── Jobs/
        └── ProcessProduct.php
    
    routes/
    └── api.php
    

    You don't need all of these directories on day one.

    Start simple and introduce additional layers when the application actually needs them.


    Example Complete Product API

    A simple route:

    Route::apiResource(
        'products',
        ProductController::class
    );
    

    Controller:

    class ProductController extends Controller
    {
        public function index()
        {
            return ProductResource::collection(
                Product::latest()->paginate(20)
            );
        }
    
        public function show(Product $product)
        {
            return new ProductResource($product);
        }
    
        public function store(StoreProductRequest $request)
        {
            $product = Product::create(
                $request->validated()
            );
    
            return new ProductResource($product);
        }
    
        public function update(
            UpdateProductRequest $request,
            Product $product
        ) {
            $product->update(
                $request->validated()
            );
    
            return new ProductResource($product);
        }
    
        public function destroy(Product $product)
        {
            $product->delete();
    
            return response()->noContent();
        }
    }
    

    This gives you a clean foundation for a production CRUD API.


    Useful Artisan Commands

    Here are some commands you'll frequently use when building Laravel APIs:

    php artisan install:api
    
    php artisan make:controller Api/ProductController
    
    php artisan make:resource ProductResource
    
    php artisan make:request StoreProductRequest
    
    php artisan make:policy ProductPolicy --model=Product
    
    php artisan route:list
    

    Run your tests with:

    php artisan test
    

    Laravel 13 API Development Checklist

    Before deploying your API, verify:

    • HTTPS is enabled
    • API routes are organized
    • Controllers are separated from business logic
    • Request validation is implemented
    • API Resources control JSON output
    • Authentication is enabled where required
    • Authorization policies are implemented
    • Rate limiting is configured
    • Pagination is used for large collections
    • N+1 queries are avoided
    • Sensitive database fields are hidden
    • API errors have consistent responses
    • API versioning is considered
    • Expensive operations use queues
    • Frequently requested data is cached
    • Automated API tests exist
    • Production logs and monitoring are configured

    Conclusion

    Laravel 13 provides an excellent foundation for building modern REST APIs.

    The framework gives you the essential pieces:

    Routes
       ↓
    Controllers
       ↓
    Validation
       ↓
    Authentication
       ↓
    Authorization
       ↓
    Eloquent
       ↓
    API Resources
       ↓
    JSON
    

    For small applications, you can start with a few routes and controllers.

    As the application grows, introduce:

    • Sanctum authentication
    • API Resources
    • Form Requests
    • Policies
    • Rate limiting
    • Pagination
    • Caching
    • Queues
    • API versioning
    • Automated tests

    The most important principle is to build your API with a clear contract between the backend and its consumers.

    A well-designed Laravel API should be secure, predictable, testable, and easy for other applications to consume.

    Laravel 13's continued improvements, including first-party JSON:API resources and other developer-focused APIs, make it an even stronger choice for modern backend development.


    Continue Learning

    If you're building a production Laravel 13 application, continue with these related guides:

    Advertisement

    Share this post

    Comments

    Loading comments...

    Related articles

    Follow new notes

    Use the RSS feed in your reader. For questions, start a WhatsApp conversation.

    RSS feed