/ JSON Response

JSON Response

Miko's JsonResponse class provides standardized JSON API responses with proper HTTP status codes.


Response Methods

Method Status Description
success($data)200Successful response
created($data)201Resource created
noContent()204No content (delete)
error($message, $code)4xx/5xxError response
notFound($message)404Resource not found
unauthorized($message)401Authentication required
forbidden($message)403Access denied
validationError($errors)422Validation failed
paginated($items, ...)200Paginated response

Success Responses

Basic Success

use Miko\Core\Http\JsonResponse;

// Simple success
JsonResponse::success(['message' => 'Operation completed']);

// Output:
// {
//     "success": true,
//     "data": {
//         "message": "Operation completed"
//     }
// }

Return Data

// Return user data
$user = User::find(1);
JsonResponse::success($user->toArray());

// Return collection
$users = User::where('IsActive', true)->get();
JsonResponse::success(array_map(fn($u) => $u->toArray(), $users));

Created (201)

$user = User::create([
    'Name' => 'John Doe',
    'Email' => 'john@example.com'
]);

JsonResponse::created($user->only('Id', 'Name', 'Email'));

// Output:
// HTTP 201 Created
// {
//     "success": true,
//     "data": {
//         "Id": 1,
//         "Name": "John Doe",
//         "Email": "john@example.com"
//     }
// }

No Content (204)

$user = User::find(1);
$user->delete();

JsonResponse::noContent();

// Output:
// HTTP 204 No Content
// (empty body)

Error Responses

Generic Error

JsonResponse::error('Something went wrong', 500);

// Output:
// HTTP 500 Internal Server Error
// {
//     "success": false,
//     "error": {
//         "message": "Something went wrong",
//         "code": 500
//     }
// }

Not Found (404)

$user = User::find($id);

if (!$user) {
    JsonResponse::notFound('User not found');
    return;
}

// Output:
// HTTP 404 Not Found
// {
//     "success": false,
//     "error": {
//         "message": "User not found",
//         "code": 404
//     }
// }

Unauthorized (401)

$token = $_SERVER['HTTP_AUTHORIZATION'] ?? '';

if (!$token) {
    JsonResponse::unauthorized('Authentication required');
    return;
}

// Output:
// HTTP 401 Unauthorized
// {
//     "success": false,
//     "error": {
//         "message": "Authentication required",
//         "code": 401
//     }
// }

Forbidden (403)

if ($user->Role !== 'admin') {
    JsonResponse::forbidden('Admin access required');
    return;
}

Validation Error (422)

$validator = new Validator($data);
$validator->validate([
    'email' => 'required|email',
    'password' => 'required|min:8'
]);

if ($validator->fails()) {
    JsonResponse::validationError($validator->errors());
    return;
}

// Output:
// HTTP 422 Unprocessable Entity
// {
//     "success": false,
//     "error": {
//         "message": "Validation failed",
//         "code": 422,
//         "errors": {
//             "email": ["The email field is required"],
//             "password": ["Password must be at least 8 characters"]
//         }
//     }
// }

Paginated Response

$page = (int)($_GET['page'] ?? 1);
$perPage = (int)($_GET['per_page'] ?? 20);

$result = User::where('IsActive', true)
    ->orderBy('Name')
    ->paginate($perPage, $page);

JsonResponse::paginated(
    $result['data'],
    [
        'current_page' => $result['current_page'],
        'last_page' => $result['last_page'],
        'total' => $result['total'],
        'per_page' => $result['per_page'],
    ]
);

// Output:
// {
//     "success": true,
//     "data": [...],
//     "pagination": {
//         "current_page": 1,
//         "total_pages": 5,
//         "total_count": 100,
//         "per_page": 20,
//         "has_next": true,
//         "has_prev": false
//     }
// }

Custom Response

JsonResponse::send([
    'success' => true,
    'data' => $data,
    'meta' => [
        'version' => '1.0',
        'timestamp' => time()
    ]
], 200);

API Controller Example

class UserController
{
    public function index(): void
    {
        $users = User::where('IsActive', true)->get();
        JsonResponse::success(array_map(fn($u) => $u->only('Id', 'Name', 'Email'), $users));
    }
    
    public function show(int $id): void
    {
        $user = User::find($id);
        
        if (!$user) {
            JsonResponse::notFound('User not found');
            return;
        }
        
        JsonResponse::success($user->only('Id', 'Name', 'Email', 'Role'));
    }
    
    public function store(): void
    {
        $data = json_decode(file_get_contents('php://input'), true);
        
        $validator = new Validator($data);
        $validator->validate([
            'name' => 'required|string|max:100',
            'email' => 'required|email'
        ]);
        
        if ($validator->fails()) {
            JsonResponse::validationError($validator->errors());
            return;
        }
        
        $user = User::create($data);
        JsonResponse::created($user->only('Id', 'Name', 'Email'));
    }
    
    public function update(int $id): void
    {
        $user = User::find($id);
        
        if (!$user) {
            JsonResponse::notFound('User not found');
            return;
        }
        
        $data = json_decode(file_get_contents('php://input'), true);
        $user->fill($data)->save();
        
        JsonResponse::success($user->only('Id', 'Name', 'Email'));
    }
    
    public function destroy(int $id): void
    {
        $user = User::find($id);
        
        if (!$user) {
            JsonResponse::notFound('User not found');
            return;
        }
        
        $user->delete();
        JsonResponse::noContent();
    }
}

Response Headers

JsonResponse automatically sets:

Content-Type: application/json; charset=utf-8

Additional headers can be set before calling response methods:

header('X-Request-Id: ' . uniqid());
header('X-Response-Time: ' . $duration . 'ms');

JsonResponse::success($data);