nomanur/api-starter-kit

A complete Laravel API boilerplate with authentication, transformers, exception handling, and scaffolding commands

Maintainers

Package info

github.com/nomanur/laravel-api-starter-kit

Homepage

pkg:composer/nomanur/api-starter-kit

Statistics

Installs: 24

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v1.0.0 2026-05-30 16:56 UTC

This package is auto-updated.

Last update: 2026-05-30 16:58:55 UTC


README

Latest Version on Packagist Total Downloads PHP Version Laravel Version License

A complete, production-ready Laravel API boilerplate with authentication, transformers, exception handling, rate limiting, and scaffolding commands. Build APIs faster with a standardized structure and best practices built-in.

๐Ÿš€ Features

  • โœ… Standardized API Responses - Consistent JSON response format
  • โœ… Fractal Transformers - Clean data transformation layer
  • โœ… Exception Handling - Centralized error handling for all API errors
  • โœ… Rate Limiting - Built-in API rate limiting middleware
  • โœ… Authentication - Laravel Sanctum integration ready
  • โœ… Scaffolding Commands - Generate API resources with one command
  • โœ… Pagination Support - Built-in pagination with metadata
  • โœ… Caching - Optional response caching
  • โœ… CORS Support - Cross-origin request handling
  • โœ… Validation - Standardized validation error responses
  • โœ… Base Controllers & Models - Extendable foundation classes
  • โœ… Postman Export - Export API routes as a Postman Collection with one command

๐Ÿ“ฆ Installation

Requirements

  • PHP 8.0 or higher
  • Laravel 9.0 or higher
  • Composer

Install via Composer

composer require nomanur/api-starter-kit

Quick Setup

Run the installation command to set up everything automatically:

php artisan api-starter-kit:install --sanctum --migrations

This will:

  • Publish the configuration file
  • Install Laravel Sanctum for authentication
  • Publish database migrations
  • Configure exception handling
  • Register middleware
  • Create helper functions

Manual Setup

If you prefer manual setup:

  1. Publish the configuration file:
php artisan vendor:publish --tag=api-starter-kit-config
  1. Add the service provider to config/app.php (if not auto-discovered):
'providers' => [
    // ...
    LaravelApi\StarterKit\ApiStarterKitServiceProvider::class,
],
  1. Add the facade alias to config/app.php:
'aliases' => [
    // ...
    'ApiBoilerplate' => LaravelApi\StarterKit\ApiBoilerplateFacade::class,
],
  1. Register middleware in bootstrap/app.php:
->withMiddleware(function (Middleware $middleware) {
    $middleware->alias([
        'api.auth' => \LaravelApi\StarterKit\Http\Middleware\ApiAuthenticate::class,
        'api.rate_limit' => \LaravelApi\StarterKit\Http\Middleware\ApiRateLimit::class,
        'api.cors' => \LaravelApi\StarterKit\Http\Middleware\ApiCors::class,
    ]);
})

๐Ÿ“– Usage

Creating Your First API Resource

The easiest way to create a complete API resource is using the artisan command:

php artisan make:api-resource Post

This will create:

  • Model: app/Models/Post.php
  • Controller: app/Http/Controllers/Api/PostsController.php
  • Transformer: app/Transformers/PostTransformer.php
  • Routes: Added to routes/api.php

You can also specify custom names:

php artisan make:api-resource Post --model=Article --controller=ArticlesController --transformer=ArticleTransformer

To create a migration along with the API resource:

php artisan make:api-resource Post --migration

This will also create a migration file in database/migrations/ with a basic table structure (id and timestamps).

API Response Format

All API responses follow a standardized format:

Success Response:

{
    "data": {
        "id": 1,
        "title": "My Post",
        "content": "Post content here",
        "created_at": "2024-01-01T00:00:00+00:00"
    },
    "message": "Post retrieved successfully"
}

Error Response:

{
    "error": "Validation failed",
    "errors": {
        "title": ["The title field is required."],
        "content": ["The content field must be at least 10 characters."]
    }
}

Paginated Response:

{
    "data": [...],
    "message": "Posts retrieved successfully",
    "meta": {
        "current_page": 1,
        "last_page": 5,
        "per_page": 15,
        "total": 75,
        "from": 1,
        "to": 15
    },
    "links": {
        "self": "http://example.com/api/v1/posts?page=1",
        "first": "http://example.com/api/v1/posts?page=1",
        "last": "http://example.com/api/v1/posts?page=5",
        "next": "http://example.com/api/v1/posts?page=2",
        "prev": null
    }
}

Using the Base Controller

Extend the ApiController in your controllers to get all the helper methods:

<?php

namespace App\Http\Controllers\User;

use App\Http\Controllers\ApiController;
use App\Models\User;
use App\Transformers\UserTransformer;
use Illuminate\Http\Request;
use Illuminate\Http\Response;

class UserController extends ApiController
{
    public function __construct()
    {
        parent::__construct();

        $this->middleware('transform.input:'.UserTransformer::class)->only(['store', 'update']);
    }

    /**
     * Display a listing of the resource.
     */
    public function index()
    {
        $users = User::all();
        return $this->showAll($users);
    }

    public function store(Request $request)
    {
        $rules = [
            'name' => 'required',
            'email' => 'required|email|unique:users',
            'password' => 'required|min:6|confirmed',
        ];

        $this->validate($request, $rules);

        $data = $request->except('password_confirmation');
        $data['password'] = bcrypt($request->password);
        $data['verified'] = User::UNVERIFIED_USER;
        $data['verification_token'] = User::generateVerificationCode();
        $data['admin'] = User::REGULAR_USER;

        $user = User::create($data);

        return $this->showOne($user, 201);
    }

    public function show(User $user)
    {
        return $this->showOne($user);
    }

    public function update(Request $request, User $user)
    {
        $rules = [
            'email' => 'email|unique:users,email,'.$user->id,
            'password' => 'min:6|confirmed',
            'admin' => 'in:'.User::ADMIN_USER.','.User::REGULAR_USER,
        ];

        $this->validate($request, $rules);

        if ($request->has('name')) {
            $user->name = $request->name;
        }

        if ($request->has('email') && $user->email != $request->email) {
            $user->verified = User::UNVERIFIED_USER;
            $user->verification_token = User::generateVerificationCode();
            $user->email = $request->email;
        }

        if ($request->has('password')) {
            $user->password = bcrypt($request->password);
        }

        if ($request->has('admin')) {
            if (!$user->isVerified()) {
                return $this->errorResponse('Only verified users can modify the admin field', 409);
            }

            $user->admin = $request->admin;
        }

        if (!$user->isDirty()) {
            return $this->errorResponse('You need to specify a different value to update', 422);
        }

        $user->save();

        return $this->showOne($user);
    }

    public function destroy(User $user)
    {
        $user->delete();
        return $this->showOne($user);
    }

    public function showMessage(string $message, int $code = 200)
    {
        return $this->successResponse(['data' => $message], $code);
    }
}

Using Transformers

Transformers provide a clean way to format your API responses:

<?php

namespace App\Transformers;

use App\Models\Post;
use LaravelApi\StarterKit\Transformers\BaseTransformer;

class PostTransformer extends BaseTransformer
{
    public function transform(Post $post): array
    {
        return [
            'id' => $post->id,
            'title' => $post->title,
            'content' => $post->content,
            'author' => $post->author?->name,
            'published' => $post->published_at?->toIso8601String(),
            'created_at' => $post->created_at->toIso8601String(),
            'updated_at' => $post->updated_at->toIso8601String(),
        ];
    }

    /**
     * Map transformed attributes to original attributes.
     */
    public static function originalAttribute(string $index): ?string
    {
        $attributes = [
            'id' => 'id',
            'title' => 'title',
            'content' => 'content',
            'author' => 'author',
            'published' => 'published_at',
            'created_at' => 'created_at',
            'updated_at' => 'updated_at',
        ];

        return $attributes[$index] ?? null;
    }

    /**
     * Map original attributes to transformed attributes.
     */
    public static function transformedAttribute(string $index): ?string
    {
        $attributes = [
            'id' => 'id',
            'title' => 'title',
            'content' => 'content',
            'author' => 'author',
            'published_at' => 'published',
            'created_at' => 'created_at',
            'updated_at' => 'updated_at',
        ];

        return $attributes[$index] ?? null;
    }
}

Using the Model

Extend ApiModel for automatic transformer support:

<?php

namespace App\Models;

use LaravelApi\StarterKit\Models\ApiModel;
use Illuminate\Database\Eloquent\Factories\HasFactory;

class Post extends ApiModel
{
    use HasFactory;

    public static $transformer = \App\Transformers\PostTransformer::class;

    protected $fillable = ['title', 'content', 'published_at'];

    protected $casts = [
        'published_at' => 'datetime',
    ];
}

API Routes

Define your API routes in routes/api.php. You can use the api_version() helper to automatically apply the version prefix from your config:

use Illuminate\Support\Facades\Route;
use App\Http\Controllers\Api\PostsController;

// Group routes by version
Route::prefix(api_version())->group(function () {
    // Public routes
    Route::get('/posts', [PostsController::class, 'index']);
    Route::get('/posts/{post}', [PostsController::class, 'show']);

    // Protected routes
    Route::middleware('api.auth')->group(function () {
        Route::apiResource('posts', PostsController::class)->except(['index', 'show']);
    });
});

Helper Functions

The package provides helper functions for quick API responses:

// Success response
api_response($data, 'Success message', 200);

// Error response
api_error('Error message', 400, $errors);

// Paginated response
api_paginated($paginator, 'Success message', 200);

โš™๏ธ Configuration

Publish the configuration file to customize settings:

php artisan vendor:publish --tag=api-starter-kit-config

Configuration options in config/api-starter-kit.php:

  • prefix: API URL prefix (default: api)
  • version: API version (default: v1)
  • rate_limit: Rate limiting settings
  • auth: Authentication driver configuration
  • response: Response format keys
  • cache: Caching settings
  • exceptions: Exception handling settings
  • validation: Validation error format
  • postman: Postman collection export settings

๐Ÿ”’ Authentication

Using Sanctum (Recommended)

  1. Install Sanctum:
php artisan api-starter-kit:install --sanctum
  1. Protect routes with middleware:
Route::middleware('api.auth')->group(function () {
    Route::get('/user', function (Request $request) {
        return $request->user();
    });
});
  1. Authenticate users via tokens:
curl -H "Authorization: Bearer YOUR_TOKEN" http://example.com/api/v1/user

๐Ÿ›ก๏ธ Middleware

The package includes three middleware classes:

  • ApiAuthenticate: Handles API authentication
  • ApiRateLimit: Rate limiting for API endpoints
  • ApiCors: CORS headers for cross-origin requests

Apply them to routes:

Route::middleware(['api.auth', 'api.rate_limit'])->group(function () {
    // Protected and rate-limited routes
});

๐ŸŽฏ Query Parameters

The API supports various query parameters for filtering and pagination:

  • per_page: Items per page (default: 15, max: 100)
  • page: Page number
  • sort_by: Field to sort by
  • desc: Sort in descending order (true/false)
  • Custom filters based on transformer attributes

Example:

GET /api/v1/posts?per_page=10&page=2&sort_by=created_at&desc=true

๐Ÿ“ Exception Handling

All exceptions are automatically caught and returned as JSON:

  • ValidationException (422): Validation errors
  • ModelNotFoundException (404): Resource not found
  • AuthenticationException (401): Unauthenticated
  • AuthorizationException (403): Unauthorized
  • NotFoundHttpException (404): Route not found
  • MethodNotAllowedHttpException (405): Invalid HTTP method
  • QueryException (409/500): Database errors

๐Ÿ“ฎ Postman Collection Export

Export all your registered API routes as a Postman Collection v2.1 JSON file with a single command. The exported collection can be imported directly into Postman for API testing and documentation.

Basic Usage

php artisan api:export

This will scan all registered API routes, organize them into folders, and generate a postman_collection.json file in storage/app/.

Alias: You can also use php artisan api:export-postman

Command Options

Option Description Default
--output= Custom output file path storage/app/postman_collection.json
--name= Collection name App name from config
--bearer= Default Bearer token for authenticated routes {{auth_token}} (Postman variable)
--base-url= Base URL override {{base_url}} (Postman variable)
--group-by= Grouping strategy: prefix or middleware prefix
--include= Regex pattern to include only matching routes All routes
--exclude= Regex pattern to exclude matching routes None
--force Overwrite existing file without confirmation Prompts

Examples

# Export with a custom collection name and output path
php artisan api:export --name="My API v2" --output=docs/api.json

# Pre-fill Bearer token for authenticated routes
php artisan api:export --bearer="your-token-here"

# Group routes by middleware (Authenticated vs Public)
php artisan api:export --group-by=middleware

# Only export user and post routes
php artisan api:export --include="users|posts"

# Exclude health check and debug routes
php artisan api:export --exclude="health|debug"

# Force overwrite without confirmation
php artisan api:export --force

What Gets Exported

The command automatically:

  • Organizes routes into folders by URL prefix (e.g., all /api/users/* routes in a "Users" folder)
  • Generates readable names from route names or URI patterns (e.g., GET /api/users/{id} โ†’ "Get User")
  • Converts path parameters from Laravel {param} syntax to Postman :param format
  • Detects authentication middleware (auth:sanctum, auth:api, auth, api.auth) and adds Bearer token auth
  • Adds JSON body templates for POST, PUT, and PATCH requests
  • Sets default headers (Accept: application/json, Content-Type: application/json)
  • Uses Postman variables for {{base_url}} and {{auth_token}} for easy environment switching

Configuration

Customize the export behavior in config/api-starter-kit.php:

'postman' => [
    'collection_name' => env('API_POSTMAN_COLLECTION_NAME', null),
    'base_url' => env('API_POSTMAN_BASE_URL', '{{base_url}}'),
    'auth_middleware' => ['auth:sanctum', 'auth:api', 'auth', 'api.auth'],
    'headers' => [
        'Accept' => 'application/json',
        'Content-Type' => 'application/json',
    ],
    'structured' => true,
    'output_path' => 'postman_collection.json',
],
Config Key Description Default
collection_name Name of the Postman collection App name
base_url Base URL for all requests (supports Postman variables) {{base_url}}
auth_middleware Middleware names that indicate authenticated routes ['auth:sanctum', 'auth:api', 'auth', 'api.auth']
headers Default headers added to every request Accept + Content-Type as JSON
structured Whether to organize routes into folders true
output_path Default output path relative to storage/app/ postman_collection.json

Output Summary

After export, the command displays a formatted table:

๐Ÿ“ฆ Exporting Postman Collection...

โœ… Postman Collection exported successfully!

+--------+---------------------+------------------+-------------------+
| Method | URI                 | Name             | Middleware        |
+--------+---------------------+------------------+-------------------+
| GET    | api/health          | โ€”                | โ€”                 |
| GET    | api/users           | users.index      | auth:sanctum      |
| POST   | api/users           | users.store      | auth:sanctum      |
| GET    | api/users/{user}    | users.show       | auth:sanctum      |
| PUT    | api/users/{user}    | users.update     | auth:sanctum      |
| DELETE | api/users/{user}    | users.destroy    | auth:sanctum      |
+--------+---------------------+------------------+-------------------+

๐Ÿ“„ Collection: My API Collection
๐Ÿ“ File: /path/to/storage/app/postman_collection.json
๐Ÿ”ข Routes exported: 6
๐Ÿ“ฆ File size: 4.52 KB

๐Ÿงช Testing

Run the package tests:

composer test

๐Ÿ“š Examples

Complete CRUD API Example

Check the example files in the package to see a complete implementation.

Health Check Endpoint

The package includes a health check endpoint:

GET /health

Response:

{
    "status": "ok",
    "timestamp": "2024-01-01T00:00:00+00:00",
    "version": "v1"
}

๐Ÿ”ง Advanced Usage

Custom Response Keys

Configure response keys in config/api-starter-kit.php:

'response' => [
    'success_key' => 'data',
    'message_key' => 'message',
    'error_key' => 'error',
    'meta_key' => 'meta',
    'links_key' => 'links',
],

Enable Caching

Enable response caching in config:

'cache' => [
    'enabled' => true,
    'ttl' => 60, // minutes
],

Custom Rate Limits

Configure rate limiting:

'rate_limit' => [
    'enabled' => true,
    'max_attempts' => 60,
    'decay_minutes' => 1,
],

๐Ÿค Contributing

Please see CONTRIBUTING for details.

๐Ÿ” Security

If you discover any security related issues, please email nomanurrahman@gmail.com instead of using the issue tracker.

๐Ÿ“„ License

The MIT License (MIT). Please see License File for more information.

๐Ÿ‘ฅ Credits

๐Ÿ™ Support

If you find this package helpful, please โญ star it on GitHub!

For questions and support:

Happy API Building! ๐Ÿš€