gponty/symfony-monday-api-bundle
A Symfony bundle for integrating the Monday.com API into your applications. It provides service configuration and convenient access to Monday.com boards, items, and GraphQL requests within Symfony's dependency injection container.
The first thing to internalize: this is a Symfony bundle, not a Laravel package. There is no service provider, no publishable config/monday.php, no facade. It ships a Bundle subclass, a DI extension, and YAML service definitions that assume symfony/framework-bundle is booting the container. In a Laravel app you get none of that wiring for free — you either bridge it manually or lift the underlying client out and bind it yourself. Release 1.20 (SF 8) reinforces this: the only change is Symfony 8 compatibility, i.e. the package's roadmap is driven by Symfony's release cycle, not Laravel's.
Steps to get moving:
composer require gponty/symfony-monday-api-bundle — but before you commit, check composer.json in the tag you install. 1.20 adds Symfony 8 support, and Symfony 8 is PHP 8.4+; confirm the php and symfony/* constraints resolve against your app's composer.json before you build anything.vendor/gponty/symfony-monday-api-bundle and read three files: the *Bundle.php class, DependencyInjection/Configuration.php (this is the authoritative list of config keys — token, API version, endpoint, maybe default board IDs), and Resources/config/services.yaml (service IDs and constructor arguments).gponty/monday-api). If so, depend on that directly and skip the bundle entirely — it's the cleaner path in Laravel, and it insulates you from Symfony major bumps like this one.Minimal Laravel bridge:
// config/monday.php
return [
'token' => env('MONDAY_API_TOKEN'),
'version' => env('MONDAY_API_VERSION', '2024-10'),
'endpoint' => 'https://api.monday.com/v2',
'boards' => [
'leads' => env('MONDAY_BOARD_LEADS'),
'deals' => env('MONDAY_BOARD_DEALS'),
],
];
// app/Providers/MondayServiceProvider.php
public function register(): void
{
$this->app->singleton(MondayClient::class, fn () => new MondayClient(
token: config('monday.token'),
apiVersion: config('monday.version'),
endpoint: config('monday.endpoint'),
));
}
First use case to exercise: read a board, then create an item from a form submission. That touches auth, a query, a mutation, and column-value serialization — the four things you will handle every day with this API.
Wrap the client; don't sprinkle it. Create App\Services\Monday\BoardRepository and ItemRepository with intent-revealing methods (createLead(array $data), moveItemToGroup(string $itemId, string $groupId)). The bundle's client is transport. Keep GraphQL strings out of controllers and jobs.
Config-driven board IDs. Board IDs differ per environment. Keep them in config/monday.php and inject via config — never hardcode them into queries.
GraphQL with reusable fragments. Fetch and cache board schema:
$query = <<<'GQL'
query ($boardId: ID!) {
boards(ids: [$boardId]) {
columns { id title type settings_str }
groups { id title }
}
}
GQL;
Columns change monthly at most; querying them on every write is wasted complexity budget. Cache for hours.
Column values are JSON strings. Nearly every mutation takes column_values: String! containing json_encode([...]). Build a small ColumnValues value object that knows the shape per type: status ({"label":"Done"} or {"id":"1"}), date (Y-m-d), people ({"personsAndTeams":[{"id":123,"kind":"person"}]}), phone ({"phone":"...","countryShortName":"US"}), email, dropdown ({"ids":[1,2]}). This is where the majority of your bugs will live.
Prefer webhooks over polling. Monday pushes item and column changes. In Laravel: a route at /webhooks/monday, an exception from VerifyCsrfToken, and a subscription command/job that calls the create_webhook mutation. The subscription handshake (Monday POSTs {"challenge": "..."} and expects it echoed back verbatim) belongs in its own early-return path, not in business logic.
Queue every write. Wrap mutations in jobs with $tries = 3, exponential backoff(), and middleware([WithoutOverlapping::class]) keyed by item ID. Monday's complexity budget is per-minute and per-token; a bulk import will 429 you quickly.
Idempotency. Persist the Monday item ID and any webhook event ID behind a unique index before doing work downstream. Retried jobs and duplicate deliveries then become no-ops.
Testing. The client almost certainly uses Guzzle or Symfony HttpClient. Mock at the HTTP layer — Http::fake() if it routes through Laravel's client, otherwise inject a Guzzle MockHandler. Do not try to boot the Symfony bundle in your test suite.
Symfony\Component\DependencyInjection; Laravel's container never calls Bundle::build() or the extension's load(). Anything autowired inside the bundle (logger, serializer, HTTP client) will not resolve. Read the service YAML and translate the constructor arguments into your own provider.Configuration.php is a TreeBuilder, not a publishable file. Don't try to "publish" it — recreate the relevant keys in config/monday.php.SF 8 compatibility bump, and that has a blast radius. The only change in this release is Symfony 8 support (PR #11). Practically: the tag likely raises the PHP floor (Symfony 8 targets PHP 8.4) and bumps symfony/* constraints. If you are on PHP 8.2/8.3, pin to the pre-1.20 tag rather than letting Composer downgrade your framework; if you're on 8.4, this release is your entry point. Either way, read composer.json at the tag — release notes won't tell you. There is no functional change to the Monday client itself, so no migration work and no behaviour change for existing users.1.072 to 1.20 is not semver-sane; treat tag names as opaque labels and pin the exact version in composer.json. Don't rely on caret ranges to protect you here.errors array. Monday returns 200 for many failures (bad column ID, unknown board, permission denied). Your wrapper must check $response['errors'] and throw, or you get silent no-ops that surface days later as missing data.429 means back off; budgets reset per minute. Log the complexity block from each response and watch it. Batch mutations (create_items, change_multiple_column_values) in chunks of roughly 10–25.API-Version. Monday ships breaking changes quarterly. Whatever the bundle defaults to may be stale — and 1.20 did not touch it. Set it explicitly and bump deliberately.Authorization header against your signing secret and reject unsigned requests in middleware.App\Services\Monday as the seam so replacing it with a hand-rolled client (or Saloon, which is idiomatic in Laravel) stays a one-day change.MondayClient over Laravel's HTTP client — query(), mutate(), retry(), throw() — often beats bridging a Symfony bundle. The bundle's real value is config schema and service wiring, and Laravel replaces both cheaply.How can I help you explore Laravel packages today?