Building a REST API
Create a JSON API with proper HTTP methods and status codes
Build a RESTful JSON API for a task management system. You'll learn how to handle JSON requests, return proper status codes, and structure API endpoints.
What You'll Build
A task management API with:
- List all tasks
- Get single task
- Create new task
- Update task
- Delete task
Time Required: 20-30 minutes
Step 1: Create the Database Table
Generate Migration
php artisan make:migration create_tasks_tableWrite the SQL
Open the migration file and add:
-- Migration: create_tasks_table
CREATE TABLE IF NOT EXISTS tasks (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title VARCHAR(255) NOT NULL,
description TEXT,
completed BOOLEAN DEFAULT 0,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX IF NOT EXISTS idx_tasks_completed ON tasks(completed);Run Migration
php artisan migrateStep 2: Create API Helper Functions
Create helper functions for JSON responses.
Create framework/Core/api.php:
<?php
function json_response($data, $status = 200)
{
http_response_code($status);
header('Content-Type: application/json');
echo json_encode($data);
exit;
}
function json_error($message, $status = 400)
{
json_response(['error' => $message], $status);
}
function json_success($data, $status = 200)
{
json_response(['success' => true, 'data' => $data], $status);
}
function json_input()
{
$input = file_get_contents('php://input');
return json_decode($input, true) ?? [];
}Then require it in framework/Core/functions.php:
require __DIR__ . '/api.php';Step 3: Create API Controllers
Create controllers for each API endpoint.
Create Directory
mkdir -p app/Http/controllers/api/tasksIndex Controller (GET /api/tasks)
Create app/Http/controllers/api/tasks/index.php:
<?php
$db = App::resolve(Core\Database::class);
$tasks = $db->query('SELECT * FROM tasks ORDER BY created_at DESC')->get();
json_success($tasks);Show Controller
Get a single task by ID.
Create app/Http/controllers/api/tasks/show.php:
<?php
$db = App::resolve(Core\Database::class);
$task = $db->query('SELECT * FROM tasks WHERE id = ?', [$_GET['id']])->find();
if (!$task) {
json_error('Task not found', 404);
}
json_success($task);Store Controller (POST /api/tasks)
Create app/Http/controllers/api/tasks/store.php:
<?php
$db = App::resolve(Core\Database::class);
$input = json_input();
// Validate
if (empty($input['title'])) {
json_error('Title is required', 422);
}
// Insert
$db->query(
'INSERT INTO tasks (title, description, completed) VALUES (?, ?, ?)',
[
$input['title'],
$input['description'] ?? null,
isset($input['completed']) ? 1 : 0
]
);
// Get created task
$id = $db->connection->lastInsertId();
$task = $db->query('SELECT * FROM tasks WHERE id = ?', [$id])->find();
json_success($task, 201);Update Controller
Update an existing task.
Create app/Http/controllers/api/tasks/update.php:
<?php
$db = App::resolve(Core\Database::class);
$input = json_input();
// Check if task exists
$task = $db->query('SELECT * FROM tasks WHERE id = ?', [$_GET['id']])->find();
if (!$task) {
json_error('Task not found', 404);
}
// Validate
if (isset($input['title']) && empty($input['title'])) {
json_error('Title cannot be empty', 422);
}
// Update
$db->query(
'UPDATE tasks SET
title = COALESCE(?, title),
description = COALESCE(?, description),
completed = COALESCE(?, completed),
updated_at = CURRENT_TIMESTAMP
WHERE id = ?',
[
$input['title'] ?? null,
$input['description'] ?? null,
isset($input['completed']) ? ($input['completed'] ? 1 : 0) : null,
$_GET['id']
]
);
// Get updated task
$task = $db->query('SELECT * FROM tasks WHERE id = ?', [$_GET['id']])->find();
json_success($task);Destroy Controller
Delete a task.
Create app/Http/controllers/api/tasks/destroy.php:
<?php
$db = App::resolve(Core\Database::class);
// Check if task exists
$task = $db->query('SELECT * FROM tasks WHERE id = ?', [$_GET['id']])->find();
if (!$task) {
json_error('Task not found', 404);
}
// Delete
$db->query('DELETE FROM tasks WHERE id = ?', [$_GET['id']]);
json_success(['message' => 'Task deleted successfully']);Step 4: Add API Routes
Register all API routes in routes/routes.php:
<?php
global $router;
// Existing routes
$router->get('/', 'welcome.php');
// API routes
$router->get('/api/tasks', 'api/tasks/index.php');
$router->get('/api/tasks/{id}', 'api/tasks/show.php');
$router->post('/api/tasks', 'api/tasks/store.php');
$router->patch('/api/tasks/{id}', 'api/tasks/update.php');
$router->delete('/api/tasks/{id}', 'api/tasks/destroy.php');Step 5: Test Your API
Start the server and test all endpoints.
Start Server
php artisan serveTest GET /api/tasks (List All)
curl http://localhost:8000/api/tasksResponse:
{
"success": true,
"data": []
}Test POST /api/tasks (Create)
curl -X POST http://localhost:8000/api/tasks \
-H "Content-Type: application/json" \
-d '{"title":"Buy groceries","description":"Milk, eggs, bread"}'Response:
{
"success": true,
"data": {
"id": 1,
"title": "Buy groceries",
"description": "Milk, eggs, bread",
"completed": 0,
"created_at": "2024-03-15 12:00:00",
"updated_at": "2024-03-15 12:00:00"
}
}Test: Get Single Task
curl http://localhost:8000/api/tasks/1Response:
{
"success": true,
"data": {
"id": 1,
"title": "Buy groceries",
"description": "Milk, eggs, bread",
"completed": 0,
"created_at": "2024-03-15 12:00:00",
"updated_at": "2024-03-15 12:00:00"
}
}Test: Update Task
curl -X PATCH http://localhost:8000/api/tasks/1 \
-H "Content-Type: application/json" \
-d '{"completed":true}'Response:
{
"success": true,
"data": {
"id": 1,
"title": "Buy groceries",
"description": "Milk, eggs, bread",
"completed": 1,
"created_at": "2024-03-15 12:00:00",
"updated_at": "2024-03-15 12:05:00"
}
}Test: Delete Task
curl -X DELETE http://localhost:8000/api/tasks/1Response:
{
"success": true,
"data": {
"message": "Task deleted successfully"
}
}Adding Authentication
To protect your API, add authentication middleware:
// In routes
$router->post('/api/tasks', 'api/tasks/store.php')->only('auth');
$router->patch('/api/tasks/{id}', 'api/tasks/update.php')->only('auth');
$router->delete('/api/tasks/{id}', 'api/tasks/destroy.php')->only('auth');For token-based auth, check the Authorization header in your middleware.
What's Next
You've built a complete REST API with proper HTTP methods and status codes. The helper functions (json_success, json_error) keep responses consistent. From here you can add authentication, rate limiting, or pagination.
Done! You now understand RESTful design, JSON handling, and API structure.