API Usage Sampleο
A complete RESTful CRUD API with JWT authentication built on LavaLust.
All endpoints return JSON. Protected endpoints require a Bearer token
in the Authorization header.
Project Structureο
app/
βββ config/
β βββ routes.php β Route definitions
βββ controllers/
β βββ AuthController.php β Register, login, refresh, logout
β βββ UsersController.php β CRUD endpoints
βββ helpers/
β βββ jwt_helper.php β jwt_encode() / jwt_decode()
βββ middleware/
β βββ JwtAuthMiddleware.php β Bearer token verification
βββ models/
βββ User_model.php β Database operations
database/
βββ migration.sql β Table schema
Setupο
1. Run the migration
CREATE TABLE IF NOT EXISTS `users` (
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`name` VARCHAR(100) NOT NULL,
`email` VARCHAR(150) NOT NULL,
`password` VARCHAR(255) NOT NULL,
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uq_users_email` (`email`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
2. Add JWT config values to app/config/config.php
<?php
// Strong random string β change before going to production
$config['jwt_secret'] = 'your-super-secret-key-change-me';
// Token lifetime in seconds (3600 = 1 hour)
$config['jwt_ttl'] = 3600;
3. Load the JWT helper globally or per-controller
In app/config/autoload.php:
<?php
$autoload['helper'] = ['jwt'];
Or load it per-controller in __construct():
<?php
$this->load->helper('jwt');
Authenticationο
The API uses stateless JWT authentication (HS256).
After a successful login or registration the response includes an
access_token. Send it on every protected request:
Authorization: Bearer <access_token>
The middleware JwtAuthMiddleware verifies the token and stores the
decoded payload in lava_instance()->auth_user so controllers can
read it without parsing the token again.
Auth Endpointsο
POST /api/auth/registerο
Create a new account and receive a token immediately.
Request body (JSON or application/x-www-form-urlencoded)
Field |
Type |
Rules |
|---|---|---|
|
string |
Required. Max 100 characters. |
|
string |
Required. Must be a valid, unique email address. |
|
string |
Required. Minimum 8 characters. |
Example request
curl -X POST https://yourdomain.com/api/auth/register \
-H "Content-Type: application/json" \
-d '{"name":"Jane Doe","email":"jane@example.com","password":"secret123"}'
201 Created
{
"success": true,
"message": "Created",
"data": {
"id": 1,
"name": "Jane Doe",
"email": "jane@example.com",
"created_at": "2025-01-15 10:00:00",
"updated_at": "2025-01-15 10:00:00",
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...",
"token_type": "Bearer",
"expires_in": 3600
}
}
422 Validation error
{
"success": false,
"error": "Validation failed",
"errors": {
"email": "This email address is already registered."
}
}
POST /api/auth/loginο
Authenticate with email and password.
Request body
Field |
Type |
Rules |
|---|---|---|
|
string |
Required. |
|
string |
Required. |
Example request
curl -X POST https://yourdomain.com/api/auth/login \
-H "Content-Type: application/json" \
-d '{"email":"jane@example.com","password":"secret123"}'
200 OK
{
"success": true,
"message": "Login successful.",
"data": {
"id": 1,
"name": "Jane Doe",
"email": "jane@example.com",
"created_at": "2025-01-15 10:00:00",
"updated_at": "2025-01-15 10:00:00",
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...",
"token_type": "Bearer",
"expires_in": 3600
}
}
401 Unauthorized (wrong credentials)
{
"success": false,
"error": "Invalid email or password."
}
POST /api/auth/refresh πο
Issue a new token using a still-valid token. Useful for extending a session before expiry.
Headers: Authorization: Bearer <token>
200 OK β returns the same shape as login with a freshly signed token.
401 Unauthorized β token is missing, invalid, or expired.
POST /api/auth/logout πο
Confirms logout. Because JWTs are stateless the client is responsible for discarding the token. The server simply acknowledges the request.
Headers: Authorization: Bearer <token>
200 OK
{
"success": true,
"message": "Logged out successfully.",
"data": null
}
Users Endpoints πο
All /api/users endpoints require a valid Authorization: Bearer header.
GET /api/usersο
Return a paginated list of users.
Query parameters
Param |
Type |
Description |
|---|---|---|
|
int |
Page number (default: |
|
int |
Items per page, maximum |
Example request
curl https://yourdomain.com/api/users?page=1&limit=5 \
-H "Authorization: Bearer <token>"
200 OK
{
"success": true,
"message": "Success",
"data": {
"users": [
{
"id": 1,
"name": "Jane Doe",
"email": "jane@example.com",
"created_at": "2025-01-15 10:00:00",
"updated_at": "2025-01-15 10:00:00"
}
],
"pagination": {
"total": 42,
"per_page": 5,
"current_page": 1,
"last_page": 9
}
}
}
GET /api/users/{id}ο
Return a single user by numeric ID.
Example request
curl https://yourdomain.com/api/users/1 \
-H "Authorization: Bearer <token>"
200 OK
{
"success": true,
"message": "Success",
"data": {
"id": 1,
"name": "Jane Doe",
"email": "jane@example.com",
"created_at": "2025-01-15 10:00:00",
"updated_at": "2025-01-15 10:00:00"
}
}
404 Not Found
{
"success": false,
"error": "User not found."
}
POST /api/usersο
Create a new user (admin use β same validation as registration).
Request body
Same fields as POST /api/auth/register: name, email, password.
Example request
curl -X POST https://yourdomain.com/api/users \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{"name":"John Smith","email":"john@example.com","password":"password123"}'
201 Created β returns the new user object with a Location header pointing
to /api/users/{id}.
{
"success": true,
"message": "Created",
"data": {
"id": 2,
"name": "John Smith",
"email": "john@example.com",
"created_at": "2025-01-15 11:00:00",
"updated_at": "2025-01-15 11:00:00"
}
}
PUT /api/users/{id} | PATCH /api/users/{id}ο
Update a user. Both PUT and PATCH are accepted and behave
identically β only the fields you send will be changed.
Request body β all fields are optional; send only those to update:
Field |
Type |
Rules |
|---|---|---|
|
string |
Max 100 characters. |
|
string |
Valid, unique email address. |
|
string |
Minimum 8 characters. |
Example request (change name only)
curl -X PATCH https://yourdomain.com/api/users/1 \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{"name":"Jane Smith"}'
200 OK β returns the full updated user object.
{
"success": true,
"message": "User updated successfully.",
"data": {
"id": 1,
"name": "Jane Smith",
"email": "jane@example.com",
"created_at": "2025-01-15 10:00:00",
"updated_at": "2025-01-15 12:30:00"
}
}
422 β if no valid fields were provided or nothing changed.
DELETE /api/users/{id}ο
Delete a user by ID.
Note
A user cannot delete their own account. The middleware compares
auth_user['sub'] against the target {id} and returns 403
if they match.
Example request
curl -X DELETE https://yourdomain.com/api/users/2 \
-H "Authorization: Bearer <token>"
204 No Content β empty body on success.
403 Forbidden (self-delete attempt)
{
"success": false,
"error": "You cannot delete your own account."
}
Error Response Referenceο
All error responses follow the same JSON envelope:
{
"success": false,
"error": "Human-readable message."
}
Validation errors additionally include an errors object:
{
"success": false,
"error": "Validation failed",
"errors": {
"email": "Please provide a valid email address.",
"password": "Password must be at least 8 characters."
}
}
Status |
When it occurs |
|---|---|
|
Resource created successfully |
|
Resource deleted (no body) |
|
General bad request |
|
Missing, invalid, or expired token; wrong credentials |
|
Authenticated but not permitted (e.g. self-delete) |
|
Resource does not exist |
|
HTTP method not allowed for this route |
|
Validation failed (see |
|
Unexpected server error |
JWT Token Structureο
Tokens are signed with HS256. The payload contains:
Claim |
Description |
|---|---|
|
User ID (integer) |
|
Userβs email address |
|
Issued-at timestamp (Unix) |
|
Expiry timestamp (Unix) β |
The decoded payload is available in any protected controller as:
<?php
$auth_user = lava_instance()->auth_user;
$user_id = $auth_user['sub'];
$email = $auth_user['email'];
Security Notesο
Passwords are hashed with
password_hash(..., PASSWORD_BCRYPT)and verified withpassword_verify(). Plain-text passwords are never stored or logged.The login endpoint returns the same vague
"Invalid email or password."message whether the email or the password was wrong, to prevent user enumeration.Tokens are validated using
hash_equals()to prevent timing attacks on the HMAC signature.All responses include
X-Content-Type-Options: nosniff,X-Frame-Options: SAMEORIGIN, and other security headers viawith_security_headers().All API responses set
Cache-Control: no-storeviano_cache().Change
jwt_secretto a long, random string before deploying to production. Never commit it to version control β use an environment variable instead.