Skip to content

Laravel quickstart

The maintained example lives at examples/laravel/. It is the executable companion to this guide.

Initialize the Atoms project and publish the Laravel adapter configuration:

Terminal window
php artisan atoms:install

Set your deployed Worker endpoint:

ATOMS_ENDPOINT=https://your-atoms-worker.example.workers.dev
ATOMS_ENVIRONMENT=production
ATOMS_SHARED_SECRET=base64-of-32-random-bytes

ATOMS_ENDPOINT is the application-side URL for ordinary Atom RPC. ATOMS_ENVIRONMENT labels the application environment in logs; it does not select the CLI environment in atoms.json.

ATOMS_SHARED_SECRET is required and must be identical on this application and the Worker. Set it on the Worker with vendor/bin/atoms shared-secret:set, not with atoms:install or secrets:set. See Secrets and authentication for generating it, what it authenticates, and how to rotate it.

Terminal window
php artisan make:atom GameRoom --with-migration
namespace App\Atoms;
use Atoms\Atom;
class GameRoom extends Atom
{
public function join(string $playerId): int
{
return $this->db()->transaction(function (\Atoms\Database $db) use ($playerId): int {
$db->execute(
'INSERT INTO players (player_id, visits) VALUES (?, 1) '
. 'ON CONFLICT(player_id) DO UPDATE SET visits = visits + 1',
[$playerId],
);
return (int) $db->query(
'SELECT visits FROM players WHERE player_id = ?',
[$playerId],
)[0]['visits'];
});
}
}

Add an append-only migration beside the Atom using the layout produced by the generator:

CREATE TABLE players (
player_id TEXT PRIMARY KEY,
visits INTEGER NOT NULL DEFAULT 0
);

The facade returns a typed RPC proxy. The Atom id is the durable identity:

use App\Atoms\GameRoom;
use Atoms\Laravel\Facades\Atoms;
$count = Atoms::get(GameRoom::class, 'room-42')->join('ada');

Use atoms/testing for fast local tests of Atom behavior, migrations, callbacks, broadcasts, and timers:

Terminal window
composer require --dev atoms/testing:^0.6
use App\Atoms\GameRoom;
use Atoms\Testing\AtomHarness;
$room = AtomHarness::for(GameRoom::class, 'room-42');
self::assertSame(1, $room->invoke('join', ['ada']));
self::assertSame(2, $room->invoke('join', ['ada']));

Before deploying, run PHPStan with vendor/atoms/phpstan-rules/rules.neon included. It catches values that cannot cross the boundary and the frozen-clock hazards described in Limits.

atoms dev builds your Atoms and serves them through the real Worker runtime you scaffolded on your machine in the “Install” step. No Cloudflare account is needed:

Terminal window
vendor/bin/atoms dev --env staging --callback-url http://127.0.0.1:8000/atoms/callback

Point the application at the local Worker while it runs:

ATOMS_ENDPOINT=http://127.0.0.1:8787
ATOMS_ENVIRONMENT=staging

The shared secret takes care of itself locally: atoms dev generates one into .env when it is absent and projects it into the Worker’s .dev.vars whenever the two differ, so the local Worker and the application always agree without you handling the value.

--callback-url tells the local Worker where your application’s callback endpoint lives, so app() and dispatch() work against the php artisan serve process. --callback-url wins over everything; without it atoms dev uses ATOMS_CALLBACK_URL from the environment it was started with, then from .env.atoms.<env> beside atoms.json, then the selected environment’s callback_url. Any of them may differ from a committed production callback. --env is required and names one of your environments — dev reads that entry’s debug_endpoints, and falls back to its callback_url only when nothing nearer supplies one, which is why the flag above matters. --port moves the Worker off 8787, and --no-build reuses the bundle from the last build. See the CLI reference for the full option surface.

Terminal window
vendor/bin/atoms validate
vendor/bin/atoms build
vendor/bin/atoms deploy --env production

See Deploy for credentials, callback configuration, and propagation behavior.

The service provider registers wrappers for the commands you run most: atoms:deploy, atoms:dev, atoms:rollback, atoms:list, atoms:install and make:atom. Each shells out to the same atoms binary and forwards your options, so php artisan atoms:deploy --env production and vendor/bin/atoms deploy --env production resolve identically.

Identically is the deliberate part. Artisan runs after Laravel has loaded your application’s .env, and the wrapper hands the child the environment the command was started with rather than the one the framework built — so a local ATOMS_CALLBACK_URL in your .env is not a deployment input, while one from your shell or from CI still is. Note also that Laravel reads its own --env off the command line, so --env production makes it load .env.production; that no longer decides anything on the Atoms side. See Framework commands read the same sources.