ApnaPHP

Documentation

Your First API

Let's build your first API endpoint in ApnaPHP! We'll create a simple CRUD API for managing tasks.

1. Create a Model

First, let's create a Task model with auto-migration:

php apna make:model Task

This creates models/Task.php. Update it with your schema:

<?php

namespace App\Models;

use ApnaPHP\Database\Model;

class Task extends Model
{
    protected string $table = 'tasks';
    protected string $primaryKey = 'id';
    protected bool $autoMigrate = true; // βœ… Auto-creates table on first use

    protected array $schema = [
        'title' => 'required|type:string|length:255',
        'description' => 'nullable|type:text',
        'status' => 'type:string|default:pending|length:50',
        'priority' => 'type:string|default:medium|length:50',
        'due_date' => 'nullable|type:date',
        'completed_at' => 'nullable|type:datetime',
    ];

    protected array $fillable = [
        'title', 'description', 'status', 'priority', 'due_date', 'completed_at'
    ];

    protected array $casts = [];

    /**
     * Check if task is completed
     */
    public function isCompleted(): bool
    {
        return $this->status === 'completed';
    }

    /**
     * Check if task is overdue
     */
    public function isOverdue(): bool
    {
        if (!$this->due_date || $this->isCompleted()) {
            return false;
        }
        return strtotime($this->due_date) < time();
    }

    /**
     * Mark task as completed
     */
    public function markAsCompleted(): void
    {
        $this->status = 'completed';
        $this->completed_at = date('Y-m-d H:i:s');
        $this->save();
    }
}

2. Create API Routes

Create your API endpoint:

php apna make:route api/tasks

This creates app/api/tasks/route.apna.php. Implement CRUD operations:

<?php

use ApnaPHP\Routing\Request;
use ApnaPHP\Routing\Response;
use App\Models\Task;

/**
 * GET /api/tasks - List all tasks
 */
function GET(Request $request): Response
{
    try {
        // Get query parameters
        $status = $request->query('status');
        $priority = $request->query('priority');
        $page = $request->query('page', 1);
        $perPage = $request->query('per_page', 15);

        // Build query
        $query = Task::query();

        if ($status) {
            $query->where('status', $status);
        }

        if ($priority) {
            $query->where('priority', $priority);
        }

        // Order by created_at descending
        $query->orderBy('created_at', 'DESC');

        // Get paginated results
        $tasks = $query->limit($perPage)->offset(($page - 1) * $perPage)->get();
        $total = Task::count();

        return Response::json([
            'success' => true,
            'data' => $tasks,
            'meta' => [
                'total' => $total,
                'page' => $page,
                'per_page' => $perPage,
                'total_pages' => ceil($total / $perPage)
            ]
        ]);

    } catch (\Exception $e) {
        console_error('Failed to fetch tasks', ['error' => $e->getMessage()]);
        return Response::json([
            'success' => false,
            'error' => 'Failed to fetch tasks'
        ], 500);
    }
}

/**
 * POST /api/tasks - Create a new task
 */
function POST(Request $request): Response
{
    try {
        // Validate request
        $validated = validate($request->all(), [
            'title' => 'required|min:3|max:255',
            'description' => 'nullable|max:1000',
            'status' => 'nullable|in:pending,in_progress,completed',
            'priority' => 'nullable|in:low,medium,high',
            'due_date' => 'nullable|date'
        ]);

        // Create task
        $task = Task::create($validated);

        console_success('Task created', ['id' => $task->id]);

        return Response::json([
            'success' => true,
            'message' => 'Task created successfully',
            'data' => $task
        ], 201);

    } catch (ValidationException $e) {
        return Response::json([
            'success' => false,
            'errors' => $e->errors()
        ], 422);
    } catch (\Exception $e) {
        console_error('Failed to create task', ['error' => $e->getMessage()]);
        return Response::json([
            'success' => false,
            'error' => 'Failed to create task'
        ], 500);
    }
}

/**
 * PUT /api/tasks - Update task (bulk update or specific)
 */
function PUT(Request $request): Response
{
    try {
        $validated = validate($request->all(), [
            'id' => 'required|integer',
            'title' => 'nullable|min:3|max:255',
            'description' => 'nullable|max:1000',
            'status' => 'nullable|in:pending,in_progress,completed',
            'priority' => 'nullable|in:low,medium,high',
            'due_date' => 'nullable|date'
        ]);

        $task = Task::find($validated['id']);

        if (!$task) {
            return Response::json([
                'success' => false,
                'error' => 'Task not found'
            ], 404);
        }

        // Remove id from update data
        unset($validated['id']);

        // Update task
        foreach ($validated as $key => $value) {
            $task->$key = $value;
        }
        $task->save();

        console_success('Task updated', ['id' => $task->id]);

        return Response::json([
            'success' => true,
            'message' => 'Task updated successfully',
            'data' => $task
        ]);

    } catch (ValidationException $e) {
        return Response::json([
            'success' => false,
            'errors' => $e->errors()
        ], 422);
    } catch (\Exception $e) {
        console_error('Failed to update task', ['error' => $e->getMessage()]);
        return Response::json([
            'success' => false,
            'error' => 'Failed to update task'
        ], 500);
    }
}

/**
 * DELETE /api/tasks - Delete task
 */
function DELETE(Request $request): Response
{
    try {
        $validated = validate($request->all(), [
            'id' => 'required|integer'
        ]);

        $task = Task::find($validated['id']);

        if (!$task) {
            return Response::json([
                'success' => false,
                'error' => 'Task not found'
            ], 404);
        }

        $task->delete();

        console_success('Task deleted', ['id' => $validated['id']]);

        return Response::json([
            'success' => true,
            'message' => 'Task deleted successfully'
        ]);

    } catch (ValidationException $e) {
        return Response::json([
            'success' => false,
            'errors' => $e->errors()
        ], 422);
    } catch (\Exception $e) {
        console_error('Failed to delete task', ['error' => $e->getMessage()]);
        return Response::json([
            'success' => false,
            'error' => 'Failed to delete task'
        ], 500);
    }
}

3. Create Single Task Route

For better RESTful design, create a route for single task operations:

php apna make:route api/tasks/[id]

Update app/api/tasks/[id]/route.apna.php:

<?php

use ApnaPHP\Routing\Request;
use ApnaPHP\Routing\Response;
use App\Models\Task;

/**
 * GET /api/tasks/{id} - Get single task
 */
function GET(Request $request): Response
{
    $id = $request->param('id');

    $task = Task::find($id);

    if (!$task) {
        return Response::json([
            'success' => false,
            'error' => 'Task not found'
        ], 404);
    }

    return Response::json([
        'success' => true,
        'data' => $task
    ]);
}

/**
 * PUT /api/tasks/{id} - Update specific task
 */
function PUT(Request $request): Response
{
    $id = $request->param('id');

    try {
        $validated = validate($request->all(), [
            'title' => 'nullable|min:3|max:255',
            'description' => 'nullable|max:1000',
            'status' => 'nullable|in:pending,in_progress,completed',
            'priority' => 'nullable|in:low,medium,high',
            'due_date' => 'nullable|date'
        ]);

        $task = Task::find($id);

        if (!$task) {
            return Response::json([
                'success' => false,
                'error' => 'Task not found'
            ], 404);
        }

        // Update task
        foreach ($validated as $key => $value) {
            $task->$key = $value;
        }
        $task->save();

        console_success('Task updated', ['id' => $task->id]);

        return Response::json([
            'success' => true,
            'message' => 'Task updated successfully',
            'data' => $task
        ]);

    } catch (ValidationException $e) {
        return Response::json([
            'success' => false,
            'errors' => $e->errors()
        ], 422);
    }
}

/**
 * DELETE /api/tasks/{id} - Delete specific task
 */
function DELETE(Request $request): Response
{
    $id = $request->param('id');

    $task = Task::find($id);

    if (!$task) {
        return Response::json([
            'success' => false,
            'error' => 'Task not found'
        ], 404);
    }

    $task->delete();

    console_success('Task deleted', ['id' => $id]);

    return Response::json([
        'success' => true,
        'message' => 'Task deleted successfully'
    ]);
}

4. Test Your API

Now you can test your API endpoints:

Create Task

curl -X POST http://localhost:3000/api/tasks \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Build ApnaPHP App",
    "description": "Create a new application using ApnaPHP",
    "status": "pending",
    "priority": "high",
    "due_date": "2024-12-31"
  }'

List Tasks

curl http://localhost:3000/api/tasks
curl http://localhost:3000/api/tasks?status=pending
curl http://localhost:3000/api/tasks?priority=high&page=1

Get Single Task

curl http://localhost:3000/api/tasks/1

Update Task

curl -X PUT http://localhost:3000/api/tasks/1 \
  -H "Content-Type: application/json" \
  -d '{
    "status": "completed",
    "completed_at": "2024-01-15 10:30:00"
  }'

Delete Task

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

5. Add Authentication (Optional)

Protect your API with authentication middleware:

<?php
// app/api/tasks/middleware.apna.php

use ApnaPHP\Routing\Request;
use ApnaPHP\Routing\Response;

// Check for API token
$token = $request->header('Authorization');

if (!$token) {
    return Response::json(['error' => 'Authorization header required'], 401);
}

// Validate token (simplified example)
$validToken = env('API_TOKEN', 'your-secret-token');

if ($token !== 'Bearer ' . $validToken) {
    return Response::json(['error' => 'Invalid token'], 401);
}

// Continue to route handler

Now requests need authentication:

curl http://localhost:3000/api/tasks \
  -H "Authorization: Bearer your-secret-token"

6. Add CORS Support

Enable CORS for frontend access:

<?php
// app/api/middleware.apna.php

use ApnaPHP\Middleware\CorsMiddleware;

return new CorsMiddleware();

7. View All Routes

Check all your registered routes:

php apna routes

You should see:

πŸ”Œ API Routes:
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ METHOD  β”‚ URI                     β”‚ HANDLER                          β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ GET     β”‚ /api/tasks              β”‚ app/api/tasks/route.apna.php@GET β”‚
β”‚ POST    β”‚ /api/tasks              β”‚ app/api/tasks/route.apna.php@POSTβ”‚
β”‚ PUT     β”‚ /api/tasks              β”‚ app/api/tasks/route.apna.php@PUT β”‚
β”‚ DELETE  β”‚ /api/tasks              β”‚ app/api/tasks/route.apna.php@DELETEβ”‚
β”‚ GET     β”‚ /api/tasks/{id}         β”‚ app/api/tasks/[id]/route.apna.php@GETβ”‚
β”‚ PUT     β”‚ /api/tasks/{id}         β”‚ app/api/tasks/[id]/route.apna.php@PUTβ”‚
β”‚ DELETE  β”‚ /api/tasks/{id}         β”‚ app/api/tasks/[id]/route.apna.php@DELETEβ”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

8. Next Steps

Congratulations! You've created your first API in ApnaPHP. πŸŽ‰

Now you can:

  • Add More Features: Implement task categories, user assignments, attachments
  • Add Authentication: Protect your API with JWT or session-based auth
  • Add Pagination: Implement proper pagination for large datasets
  • Add Search: Add search and filtering capabilities
  • Add Validation: Add more complex validation rules
  • Add Relationships: Link tasks to users, projects, or categories
  • Add Testing: Write tests for your API endpoints

Useful Resources