nomanurrahman / api-starter-kit
A complete Laravel API boilerplate with authentication, transformers, exception handling, and scaffolding commands
Requires
- php: ^8.2|^8.3|^8.4
- illuminate/auth: ^10.0|^11.0|^12.0|^13.0
- illuminate/database: ^10.0|^11.0|^12.0|^13.0
- illuminate/http: ^10.0|^11.0|^12.0|^13.0
- illuminate/support: ^10.0|^11.0|^12.0|^13.0
- laravel/sanctum: ^3.0|^4.0
- spatie/laravel-fractal: ^6.0
Requires (Dev)
- orchestra/testbench: ^8.0|^9.0|^10.0|^11.0
- pestphp/pest: ^2.0|^3.0|^4.0
- phpunit/phpunit: ^10.0|^11.0|^12.0
README
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
๐ฆ Installation
Requirements
- PHP 8.0 or higher
- Laravel 9.0 or higher
- Composer
Install via Composer
composer require nomanurrahman/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:
- Publish the configuration file:
php artisan vendor:publish --tag=api-starter-kit-config
- Add the service provider to
config/app.php(if not auto-discovered):
'providers' => [ // ... LaravelApi\StarterKit\ApiStarterKitServiceProvider::class, ],
- Add the facade alias to
config/app.php:
'aliases' => [ // ... 'ApiBoilerplate' => LaravelApi\StarterKit\ApiBoilerplateFacade::class, ],
- 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
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(), ]; } 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; } public static function transformedAttribute(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; } }
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:
use Illuminate\Support\Facades\Route; use App\Http\Controllers\Api\PostsController; // Public routes Route::get('/posts', [PostsController::class, 'index']); Route::get('/posts/{post}', [PostsController::class, 'show']); // Protected routes Route::middleware('api.auth')->group(function () { Route::post('/posts', [PostsController::class, 'store']); Route::put('/posts/{post}', [PostsController::class, 'update']); Route::delete('/posts/{post}', [PostsController::class, 'destroy']); });
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
๐ Authentication
Using Sanctum (Recommended)
- Install Sanctum:
php artisan api-starter-kit:install --sanctum
- Protect routes with middleware:
Route::middleware('api.auth')->group(function () { Route::get('/user', function (Request $request) { return $request->user(); }); });
- 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 numbersort_by: Field to sort bydesc: 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
๐งช 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:
- Open an issue on GitHub
- Email: nomanurrahman@gmail.com
Happy API Building! ๐