DALT.PHP
Guides

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_table

Write 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 migrate

Step 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/tasks

Index 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 serve

Test GET /api/tasks (List All)

curl http://localhost:8000/api/tasks

Response:

{
  "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/1

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: 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/1

Response:

{
  "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.

On this page