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
- Routing - Learn more about file-based routing
- Models - Master the ORM
- Validation - Deep dive into validation
- Middleware - Protect your routes
- Request Object - Handle requests like a pro
- Response Object - Create perfect responses
