Stores - evansims/openfga-php GitHub Wiki

Stores can be thought of as your authorization database. They contain your permission rules, user relationships, and everything needed to answer "can this user do that?" Every OpenFGA operation happens within a store, making them the foundation of your authorization system.


Prerequisites

The examples in this guide assume you have the following setup:

<?php

declare(strict_types=1);

use OpenFGA\Client;

$client = new Client(
    url: $_ENV['FGA_API_URL'] ?? 'http://localhost:8080',
);


Single application setup

For a typical application, create one store per environment:

use OpenFGA\Responses\CreateStoreResponse;

use function OpenFGA\{failure, success};

$result = $client->createStore(name: 'my-app-store');

failure($result, function (Throwable $e): void {
    throw new RuntimeException("Error creating store: {$e->getMessage()}");
});

success($result, function (CreateStoreResponse $store): void {
    echo "Created store: {$store->getId()}\n";
    echo "Store name: {$store->getName()}\n";
});

Save the store ID in your environment configuration. You'll need it for future API calls.


Multi-tenant patterns

For SaaS applications, create a store per customer to ensure complete data isolation:

use function OpenFGA\store;

final class TenantStoreManager
{
    public function __construct(
        private readonly Client $client,
        private array $cache = [],
    ) {
    }

    public function getStoreForTenant(string $tenantId): string
    {
        if (! array_key_exists($tenantId, $this->cache)) {
            $this->cache[$tenantId] = store("tenant-{$tenantId}", $this->client);
        }

        return $this->cache[$tenantId];
    }
}

$tenantManager = new TenantStoreManager($client);

$acmeStoreId = $tenantManager->getStoreForTenant('acme-corp');
echo "Store for ACME Corp: {$acmeStoreId}\n";


Environment separation

Keep your environments completely isolated:

use RuntimeException;
use Throwable;

use function OpenFGA\failure;

// Environment separation
function createEnvironmentStore(Client $client, string $environment): string
{
    $result = $client->createStore(
        name: sprintf('app-%s-%s', $environment, date('Y-m-d')),
    );

    failure($result, function (Throwable $e): void {
        throw new RuntimeException('Failed to create store: ' . $e->getMessage());
    });

    return $result->unwrap()->getId();
}


Store management

Finding and managing existing stores:

$result = $client->listStores();

if ($result->succeeded()) {
    $stores = $result->unwrap()->getStores();

    echo "Available stores:\n";

    foreach ($stores as $store) {
        echo "- {$store->getName()} (ID: {$store->getId()})\n";
        echo "  Created: {$store->getCreatedAt()->format('Y-m-d H:i:s')}\n";
        echo "  Updated: {$store->getUpdatedAt()->format('Y-m-d H:i:s')}\n";
    }
}

For pagination with many stores:

// Pagination with continuation tokens
$stores = [];
$continuationToken = null;

do {
    $result = $client->listStores(
        pageSize: 10,
        continuationToken: $continuationToken,
    );

    if ($result->succeeded()) {
        $response = $result->unwrap();

        foreach ($response->getStores() as $store) {
            $stores[] = $store;
        }
        $continuationToken = $response->getContinuationToken();
    } else {
        break;
    }
} while (null !== $continuationToken);

echo 'Total stores: ' . count($stores) . "\n";


Best practices

When to use multiple stores:

  • Different environments (dev/staging/production)
  • Different customers in SaaS apps
  • Different applications with no shared permissions
  • Compliance requirements

When to use a single store:

  • Different user roles (use authorization models instead)
  • Different features in the same app (use object types)
  • A/B testing (use different object IDs)

Pro tips:

  • Start with one store per environment
  • Save store IDs in your configuration
  • Test your app works with store switching
  • Document which team owns each store
⚠️ **GitHub.com Fallback** ⚠️