A versioned key-value store built with Laravel 12 that stores JSON or string values while preserving version history. Retrieve the latest value or any historical value using a UNIX timestamp.
This project provides a lightweight HTTP API for storing versioned key-value data.
Every successful update creates a new immutable version of the value. Previous versions are preserved, allowing historical lookups using a UNIX timestamp.
POST /api/object
Store a new version for a single key.
GET /api/object/{key}
Retrieve the latest value or a historical version using an optional UNIX timestamp.
GET /api/object/get_all_records
Retrieve the latest value for every key (paginated).
No host PHP installation is required.
git clone https://sdatulayta@bitbucket.org/my-project/secretlab-store-api.git
cd secretlab-store-api
cp .env.example .env
composer install
./vendor/bin/sail up -d
./vendor/bin/sail artisan key:generate
./vendor/bin/sail artisan migrate --force
git clone https://sdatulayta@bitbucket.org/my-project/secretlab-store-api.git
cd secretlab-store-api
Ensure Docker Desktop is running.
cp .env.example .env
composer install
or
./vendor/bin/sail composer install
./vendor/bin/sail up -d
If Sail is not installed:
composer require laravel/sail --dev
./vendor/bin/sail up -d
./vendor/bin/sail artisan key:generate
Ensure .env contains the correct database credentials.
If modified, restart Sail.
./vendor/bin/sail down
./vendor/bin/sail up -d
./vendor/bin/sail artisan migrate --force
Local URL
http://localhost/api/
Public URL
https://secretlab.stifd.com/api/
APP_ENV=local
APP_DEBUG=true
DB_CONNECTION=mysql
DB_HOST=mysql
DB_PORT=3306
DB_DATABASE=secretlab
DB_USERNAME=sail
DB_PASSWORD=password
CACHE_DRIVER=array
id
bigintobject_key
varchar(55)object_val
JSONobject_hash
char(64)version_timestamp
unsigned integercreated_at
timestampINDEX (object_key, version_timestamp)
Optimizes historical lookups by key and timestamp.
UNIQUE (
object_key,
object_hash,
version_timestamp
)
Prevents duplicate inserts.
/api/objectStore a new version of a key.
{
"mykey": {
"name": "Stephen"
}
}
Rules
A-Z
a-z
0-9
_
-
.
Value cannot be
201 Created
{
"message": "created",
"data": {
"version_timestamp": 1670000000
}
}
422 Unprocessable Entity
409 Conflict
/api/object/{key}Returns the latest value.
Supports historical lookup.
?timestamp=1670000000
Returns the latest record where
version_timestamp <= timestamp
{
"data": {
"name": "Version 2"
}
}
400 Bad Request
404 Not Found
/api/object/get_all_recordsReturns the latest value for every key.
page
1per_page
15100Maximum
per_page=100
{
"data": [
{
"key": "a",
"value": "v2",
"version_timestamp": 200
},
{
"key": "b",
"value": "hello",
"version_timestamp": 150
}
],
"meta": {
"total": 2,
"per_page": 15,
"current_page": 1
}
}
per_page limitcurl -X POST http://localhost/api/object \
-H "Content-Type: application/json" \
-d '{"mykey":{"name":"Stephen"}}'
Response
{
"message":"created",
"data":{
"version_timestamp":1670000000
}
}
curl http://localhost/api/object/mykey
{
"data":{
"name":"Version 2"
}
}
curl "http://localhost/api/object/mykey?timestamp=150"
{
"data":"value1"
}
curl "http://localhost/api/object/get_all_records?per_page=5&page=1"
Run migrations
./vendor/bin/sail artisan migrate:fresh --seed
Run tests
./vendor/bin/sail test
PHPUnit
./vendor/bin/sail php vendor/bin/phpunit --testdox
Recommended
This project uses Bitbucket Pipelines to automate continuous integration and continuous deployment.
The pipeline performs:
Every pipeline run performs automated validation by:
Example:
- step:
name: Run Laravel Tests
caches:
- composer
services:
- mysql
script:
- apt-get update
- apt-get install -y unzip git libzip-dev
- docker-php-ext-install zip pdo_mysql
- curl -sS https://getcomposer.org/installer | php
- mv composer.phar /usr/local/bin/composer
- composer install --no-interaction
- cp .env.example .env.testing
- sed -i 's/DB_HOST=.*/DB_HOST=localhost/' .env.testing
- sed -i 's/DB_USERNAME=.*/DB_USERNAME=root/' .env.testing
- sed -i 's/DB_PASSWORD=.*/DB_PASSWORD=root/' .env.testing
- sed -i 's/DB_DATABASE=.*/DB_DATABASE=testdb/' .env.testing
- php artisan key:generate --env=testing
- php artisan migrate --env=testing
- php artisan test
Production deployment is automatically triggered after code is pushed to the main branch.
The deployment process:
main branchExample:
- step:
name: Deploy Production
deployment: production
script:
- cat /etc/os-release
- apt-get update
- apt-cache policy openssh-client
- apt-get install -y openssh-client
- mkdir -p ~/.ssh
- ssh-keyscan secretlab.stifd.com >> ~/.ssh/known_hosts
- ssh username@secretlab.stifd.com "
cd {APP_PATH} &&
git pull origin main &&
composer install --no-dev --optimize-autoloader &&
php artisan optimize
"
Production deployment runs automatically when changes are pushed to:
main branch
Deployment flow:
Developer Push
|
v
Bitbucket Repository
|
v
CI Pipeline
|
|-- Install Dependencies
|-- Run Migrations
|-- Execute Tests
|
v
CD Pipeline
|
|-- SSH to Production Server
|-- Pull Latest Code
|-- Install Production Dependencies
|-- Optimize Laravel
|
v
Production Environment
For a production environment, consider adding:
Current optimizations
(object_key, version_timestamp)Recommendations for larger datasets
Verify
.env credentials are correct./vendor/bin/sail artisan migrate:fresh
Ensure
CACHE_DRIVER=array
./vendor/bin/sail artisan migrate:fresh
routes/api.php
app/Http/Requests/StoreObjectRequest.php
app/Http/Controllers/Api/ObjectController.php
app/Services/ObjectService.php
app/Repositories/ObjectRepository.php
app/Models/KeyValue.php
database/migrations/2026_07_15_222701_create_objects_table.php
tests/Feature/ObjectApiTest.php
This project was created solely for the Secretlab Technical Exercise.
It is intended for evaluation purposes only.
To test the API quickly, please download the provided Postman collection.
๐ฅ Download Postman Collection
Import the collection into Postman