A complete REST API built with Laravel, featuring authentication, data transformation, and comprehensive documentation.
rest-api/
โโโ app/
โ โโโ Http/Controllers/Api/
โ โ โโโ PostController.php # API controller
โ โโโ Http/Resources/
โ โ โโโ PostResource.php # Data transformation
โ โ โโโ PostCollection.php # Collection wrapper
โ โโโ Http/Requests/
โ โ โโโ StorePostRequest.php # Validation rules
โ โโโ Models/
โ โโโ Post.php # Eloquent model
โโโ database/
โ โโโ migrations/
โ โโโ create_posts_table.php # Database structure
โโโ routes/
โ โโโ api.php # API routes
โโโ tests/
โ โโโ Feature/
โ โโโ ApiTest.php # API tests
โโโ README.md # This file
# Clone the project
git clone <repository-url>
cd rest-api
# Install dependencies
composer install
# Copy environment file
cp .env.example .env
# Generate application key
php artisan key:generate
# Configure database in .env
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=rest_api
DB_USERNAME=root
DB_PASSWORD=
# Run migrations
php artisan migrate
# Install Laravel Sanctum
composer require laravel/sanctum
php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"
php artisan migrate
# Start server
php artisan serve
class PostController extends Controller
{
public function index(): PostCollection
{
$posts = Post::with(['user', 'category'])
->latest()
->paginate(15);
return new PostCollection($posts);
}
public function store(StorePostRequest $request): PostResource
{
$post = Post::create($request->validated());
return new PostResource($post->load(['user', 'category']));
}
public function show(Post $post): PostResource
{
return new PostResource($post->load(['user', 'category', 'comments']));
}
}
class PostResource extends JsonResource
{
public function toArray($request): array
{
return [
'id' => $this->id,
'title' => $this->title,
'slug' => $this->slug,
'content' => $this->content,
'status' => $this->status,
'published_at' => $this->published_at?->toISOString(),
'user' => new UserResource($this->whenLoaded('user')),
'category' => new CategoryResource($this->whenLoaded('category')),
'comments_count' => $this->comments_count,
'created_at' => $this->created_at->toISOString(),
'updated_at' => $this->updated_at->toISOString(),
'links' => [
'self' => route('api.posts.show', $this->id),
'comments' => route('api.posts.comments.index', $this->id),
],
];
}
}
class StorePostRequest extends FormRequest
{
public function rules(): array
{
return [
'title' => 'required|string|max:255',
'content' => 'required|string|min:100',
'category_id' => 'required|exists:categories,id',
'status' => 'required|in:draft,published',
'published_at' => 'nullable|date|after:now',
];
}
public function messages(): array
{
return [
'title.required' => 'The post title is required.',
'content.min' => 'The post content must be at least 100 characters.',
];
}
}
Route::prefix('v1')->group(function () {
// Public routes
Route::get('posts', [PostController::class, 'index']);
Route::get('posts/{post}', [PostController::class, 'show']);
// Protected routes
Route::middleware('auth:sanctum')->group(function () {
Route::post('posts', [PostController::class, 'store']);
Route::put('posts/{post}', [PostController::class, 'update']);
Route::delete('posts/{post}', [PostController::class, 'destroy']);
});
});
POST /api/auth/register
- Register new userPOST /api/auth/login
- Login userPOST /api/auth/logout
- Logout userPOST /api/auth/refresh
- Refresh tokenGET /api/posts
- List all postsPOST /api/posts
- Create new postGET /api/posts/{id}
- Get specific postPUT /api/posts/{id}
- Update postDELETE /api/posts/{id}
- Delete postGET /api/posts/{id}/comments
- Get post commentsPOST /api/posts/{id}/comments
- Add commentPUT /api/comments/{id}
- Update commentDELETE /api/comments/{id}
- Delete comment# Register a user
curl -X POST http://localhost:8000/api/auth/register \
-H "Content-Type: application/json" \
-d '{
"name": "John Doe",
"email": "john@example.com",
"password": "password123",
"password_confirmation": "password123"
}'
# Login
curl -X POST http://localhost:8000/api/auth/login \
-H "Content-Type: application/json" \
-d '{
"email": "john@example.com",
"password": "password123"
}'
# Create a post (with token)
curl -X POST http://localhost:8000/api/posts \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_TOKEN" \
-d '{
"title": "My First Post",
"content": "This is the content of my first post.",
"category_id": 1,
"status": "published"
}'
http://localhost:8000/api
// Custom API Resource
class CustomPostResource extends JsonResource
{
public function toArray($request): array
{
return [
'data' => [
'id' => $this->id,
'title' => $this->title,
// ... other fields
],
'meta' => [
'version' => '1.0',
'timestamp' => now()->toISOString(),
],
];
}
}
// Custom API middleware
class ApiResponseMiddleware
{
public function handle($request, Closure $next)
{
$response = $next($request);
return response()->json([
'success' => true,
'data' => $response->getData(),
'message' => 'Request successful',
]);
}
}
# Run tests
php artisan test
# API tests
php artisan test --filter=ApiTest
class ApiTest extends TestCase
{
use RefreshDatabase;
public function test_can_register_user()
{
$response = $this->postJson('/api/auth/register', [
'name' => 'Test User',
'email' => 'test@example.com',
'password' => 'password123',
'password_confirmation' => 'password123',
]);
$response->assertStatus(201)
->assertJsonStructure([
'data' => [
'user' => ['id', 'name', 'email'],
'token'
]
]);
}
public function test_can_create_post()
{
$user = User::factory()->create();
$token = $user->createToken('test-token')->plainTextToken;
$response = $this->withHeaders([
'Authorization' => 'Bearer ' . $token,
])->postJson('/api/posts', [
'title' => 'Test Post',
'content' => 'Test content',
'category_id' => 1,
'status' => 'published',
]);
$response->assertStatus(201);
}
}
# Create Heroku app
heroku create rest-api-laravel
# Configure environment variables
heroku config:set APP_KEY=base64:your-key
heroku config:set DB_CONNECTION=postgresql
heroku config:set SANCTUM_STATEFUL_DOMAINS=your-domain.com
# Deploy
git push heroku main
# Run migrations
heroku run php artisan migrate
# Clone on server
git clone <repository-url>
cd rest-api
# Install dependencies
composer install --optimize-autoloader --no-dev
# Configure environment
cp .env.example .env
php artisan key:generate
# Run migrations
php artisan migrate
# Optimize for production
php artisan config:cache
php artisan route:cache
{
"data": {
"id": 1,
"title": "Post Title",
"content": "Post content...",
"created_at": "2024-01-01T00:00:00.000000Z"
},
"meta": {
"current_page": 1,
"total": 10,
"per_page": 15
},
"links": {
"first": "http://localhost:8000/api/posts?page=1",
"last": "http://localhost:8000/api/posts?page=1",
"prev": null,
"next": null
}
}
{
"message": "The given data was invalid.",
"errors": {
"title": ["The title field is required."],
"content": ["The content must be at least 100 characters."]
}
}
The API includes rate limiting to prevent abuse:
// In RouteServiceProvider
Route::middleware(['auth:sanctum', 'throttle:60,1'])->group(function () {
// Protected routes with 60 requests per minute
});
git checkout -b feature/AmazingFeature
)git commit -m 'Add some AmazingFeature'
)git push origin feature/AmazingFeature
)This project is licensed under the MIT License. See the LICENSE
file for details.
For any questions or issues:
REST API - Complete Laravel REST API with authentication and documentation ๐