adaiasmagdiel/loxodontu-php

PHP client for Loxodontu, an open-source Backend-as-a-Service (BaaS).

Maintainers

Package info

github.com/AdaiasMagdiel/loxodontu-php

Homepage

pkg:composer/adaiasmagdiel/loxodontu-php

Transparency log

Statistics

Installs: 1

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v0.1.0 2026-08-29 21:56 UTC

This package is auto-updated.

Last update: 2026-08-29 21:59:47 UTC


README

PHP client for Loxodontu, an open-source Backend-as-a-Service. Framework-agnostic, no HTTP client dependency (uses ext-curl directly).

Install

composer require adaiasmagdiel/loxodontu-php

Two clients, matching Loxodontu's two token types

  • Client — app-facing. REST passthrough, edge function invocation, and end-user auth for a single project. Authenticated with a project API key.
  • Admin — platform-facing. Manage your account, projects, tables, keys, RLS policies, cron jobs, and functions. Authenticated with your platform login.

App client

use AdaiasMagdiel\Loxodontu\Client;

$loxo = new Client(
    'https://your-app.example.com/api/v1',
    'my-project', // project id / slug
    'PROJECT_API_KEY',
);

// A chain ends with an explicit ->get() (PHP has no thenable to auto-execute on await).
$response = $loxo->from('todos')
    ->select()
    ->eq('done', false)
    ->order('created_at', ascending: false)
    ->limit(20)
    ->get();

$todos = $response->data;

$loxo->from('todos')->insert(['title' => 'Write docs'])->get();
$loxo->from('todos')->update(['id' => 1, 'done' => true])->get(); // single row, id in the body
$loxo->from('todos')->update(['done' => true])->eq('done', false)->get(); // bulk, by filter
$loxo->from('todos')->delete()->eq('id', 1)->get(); // single row, by filter
$loxo->from('todos')->delete()->lt('views', 10)->get(); // bulk, by filter
$loxo->from('todos')->delete([2, 3, 4])->get(); // bulk, by id list

// End users (your app's own users, separate from your platform account)
$loxo->auth->register('user@example.com', 'password123');
$loxo->auth->login('user@example.com', 'password123'); // token stored & sent automatically
$loxo->auth->logout();

// Edge functions
$response = $loxo->functions->invoke('daily-cleanup', body: ['source' => 'client']);

Filters mirror the REST passthrough API 1:1: eq, neq, gt, gte, lt, lte, like (* wildcard), in. They work the same way on select(), update(), and delete(). For update()/delete(), in order of precedence: a list (of row arrays, or ids for delete) is always a bulk write; otherwise any chained filter (eq, gt, ...) scopes a filtered update/delete over every matching row; otherwise, for update() only, an id key in the body targets that single row.

Admin client

use AdaiasMagdiel\Loxodontu\Admin;

$admin = new Admin('https://your-app.example.com/api/v1');
$admin->auth->login('me@example.com', 'password123');

$projects = $admin->projects->list()->unwrap();
$project = $admin->projects->create(['name' => 'New project'])->unwrap();

$project1 = $admin->projects->for($project['id']);
$project1->tables->create([
    'name' => 'todos',
    'columns' => [
        ['name' => 'title', 'type' => 'text'],
        ['name' => 'done', 'type' => 'boolean', 'default_value' => false],
    ],
]);
$project1->keys->create(['name' => 'frontend', 'permissions' => ['select', 'insert']]);
$project1->tables->rlsPolicies($tableId)->create([
    'name' => 'owner can read',
    'operation' => 'SELECT',
    'conditions' => ['user_id' => '$auth.id'],
]);
$project1->sql('SELECT COUNT(*) FROM todos');

Responses

Every request resolves to the same envelope — nothing throws on an API error by default, matching the "check error" pattern of most BaaS clients:

final class LoxodontuResponse
{
    public readonly mixed $data;
    public readonly ?array $error; // ['message' => string, 'status' => int]
    public readonly ?int $count;   // from X-Total-Count on paginated list endpoints
    public readonly int $status;
}

If you'd rather throw, call ->unwrap() — it returns data, or throws a LoxodontuError:

use AdaiasMagdiel\Loxodontu\LoxodontuError;

try {
    $todos = $loxo->from('todos')->select()->get()->unwrap();
} catch (LoxodontuError $e) {
    // $e->getMessage(), $e->status()
}

Session storage

Client's end-user token and Admin's platform token are held via a TokenStorage implementation, passed as options.storage:

  • InMemoryStorage (default) — lives only for the current process; fine for a script that logs in and uses the token within the same run.
  • SessionStorage — persists the token in PHP's $_SESSION across requests, for a traditional web app.
  • Your own implementation of the TokenStorage interface (getItem/setItem/removeItem) — a cookie, cache, or database row.
use AdaiasMagdiel\Loxodontu\SessionStorage;

$loxo = new Client($url, $projectId, $apiKey, ['storage' => new SessionStorage()]);

Custom transport

Requests are sent via ext-curl by default (CurlTransport). Pass your own implementation of the Transport interface via options.transport to swap it out — useful for testing, or for routing through a different HTTP stack.

License

AGPL-3.0-only, matching Loxodontu itself.