Laravel quickstart
The maintained example lives at examples/laravel/. It is the executable companion to this guide.
Configure the adapter
Section titled “Configure the adapter”Initialize the Atoms project and publish the Laravel adapter configuration:
php artisan atoms:installSet your deployed Worker endpoint:
ATOMS_ENDPOINT=https://your-atoms-worker.example.workers.devATOMS_ENVIRONMENT=productionATOMS_SHARED_SECRET=base64-of-32-random-bytesATOMS_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.
Create an Atom
Section titled “Create an Atom”php artisan make:atom GameRoom --with-migrationnamespace 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);Call it
Section titled “Call it”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');Test it without Cloudflare
Section titled “Test it without Cloudflare”Use atoms/testing for fast local tests of Atom behavior, migrations, callbacks, broadcasts, and timers:
composer require --dev atoms/testing:^0.6use 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.
Run it locally
Section titled “Run it locally”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:
vendor/bin/atoms dev --env staging --callback-url http://127.0.0.1:8000/atoms/callbackPoint the application at the local Worker while it runs:
ATOMS_ENDPOINT=http://127.0.0.1:8787ATOMS_ENVIRONMENT=stagingThe 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.
Build and deploy
Section titled “Build and deploy”vendor/bin/atoms validatevendor/bin/atoms buildvendor/bin/atoms deploy --env productionSee Deploy for credentials, callback configuration, and propagation behavior.
Through Artisan
Section titled “Through Artisan”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.