DALT.PHP
Guides

Building a REST API

Create a JSON API with proper HTTP methods and status codes

Learn how to build a RESTful JSON API using DALT.PHP. You'll create endpoints for managing tasks with proper HTTP methods, status codes, and JSON responses.

What You'll Build

A task management API with:

  • GET /api/tasks - List all tasks
  • GET /api/tasks/\{id\} - Get single task
  • POST /api/tasks - Create new task
  • PATCH /api/tasks/\{id\} - Update task
  • DELETE /api/tasks/\{id\} - Delete task

All responses will be JSON with appropriate HTTP status codes.

Prerequisites

  • DALT.PHP installed and running
  • Basic understanding of REST APIs
  • Familiarity with JSON

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 /api/tasks/{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 (PATCH /api/tasks/{id})

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 /api/tasks/{id})

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 /api/tasks/{id} (Get One)

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 PATCH /api/tasks/{id} (Update)

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 /api/tasks/{id} (Delete)

curl -X DELETE http://localhost:8000/api/tasks/1

Response:

{
  "success": true,
  "data": {
    "message": "Task deleted successfully"
  }
}

What You Learned

Congratulations! You've built a complete REST API. Here's what you learned:

1. RESTful Design

GET    /api/tasks      → List all tasks
GET    /api/tasks/\{id\} → Get single task
POST   /api/tasks      → Create task
PATCH  /api/tasks/\{id\} → Update task
DELETE /api/tasks/\{id\} → Delete task

Each endpoint uses the appropriate HTTP method.

2. HTTP Status Codes

  • 200 OK - Successful GET, PATCH, DELETE
  • 201 Created - Successful POST
  • 404 Not Found - Resource doesn't exist
  • 422 Unprocessable Entity - Validation error

3. JSON Responses

json_success($data, 200);  // Success response
json_error($message, 404);  // Error response

Consistent response format makes the API easy to consume.

4. Request Body Parsing

$input = json_input();  // Parse JSON from request body

APIs receive data as JSON, not form data.

5. Validation

if (empty($input['title'])) {
    json_error('Title is required', 422);
}

Always validate input before saving to database.

6. Partial Updates (PATCH)

UPDATE tasks SET 
    title = COALESCE(?, title),  // Only update if provided
    description = COALESCE(?, description)
WHERE id = ?

PATCH allows updating only specific fields.

On this page