api-platform-performance
Skill2 starsChanged 6 months agoMITView on GitHub
Tune API Platform 4.3 + Doctrine + Symfony performance — recognize the 4.x default trap `eager_loading.force_eager: true` (often counter-productive, switch to `false` + targeted join fetches), use `EXTRA_LAZY` for large collections, project DTOs in DQL (`NEW App\\Dto\\PostListItem(...)`) to avoid hydrating full entities for list views, iterate in batches with `toIterable()` + `EntityManager::clear()`, set `HINT_READ_ONLY` when entities are not mutated, cache resource metadata with APCu, run **FrankenPHP in worker mode** for stateful kernel + p99 latency drops, configure HTTP cache headers per operation (`cacheHeaders`), and invalidate Varnish / Nginx via `cacheTags` + PURGE. Also covers when to add a dynamic `Last-Modified` listener on `ViewEvent`. Trigger on "slow API", "N+1", "memory blow-up on collection", "too many joins (max_joins 30)", "p99 latency", "cache invalidation", or "force_eager problem".
In this repository52 other files it gives its agents.
- api-platform-dto-resourcesSkill
- api-platform-errorsSkill
- api-platform-file-uploadSkill
- api-platform-filtersSkill
- api-platform-identifiersSkill
- api-platform-mcpSkill
- api-platform-mutatorsSkill
- api-platform-openapiSkill
- api-platform-paginationSkill
- api-platform-resilienceSkill
- api-platform-resourcesSkill
- api-platform-securitySkill
- api-platform-serializationSkill
- api-platform-state-processorsSkill
- api-platform-state-providersSkill
- api-platform-testsSkill
- api-platform-upgradeSkill
- api-platform-userSkill
- api-platform-versioningSkill
- bootstrap-checkSkill
- brainstormingSkill
- config-env-parametersSkill
- controller-cleanupSkill
- cqrs-and-handlersSkill
- daily-workflowSkill
- doctrine-batch-processingSkill
- doctrine-fetch-modesSkill
- doctrine-fixtures-foundrySkill
- doctrine-migrationsSkill
- doctrine-relationsSkill
- doctrine-transactionsSkill
- effective-contextSkill
- executing-plansSkill
- functional-testsSkill
- interfaces-and-autowiringSkill
- makefile-disciplineSkill
- messenger-retry-failuresSkill
- ports-and-adaptersSkill
- quality-checksSkill
- rate-limitingSkill
- runner-selectionSkill
- strategy-patternSkill
- symfony-cacheSkill
- symfony-messengerSkill
- symfony-schedulerSkill
- symfony-votersSkill
- tdd-with-pestSkill
- tdd-with-phpunitSkill
- test-doubles-mockingSkill
- using-symfony-superpowersSkill
- value-objects-and-dtosSkill
- writing-plansSkill
The file
We split this skill into the rules it gives an agent
2 rules. About 0 are common, 0 are distinctive, and 2 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈0
Distinctive · estimate
This author's own choices. Worth a look.
2
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 28
Add HTTP cache headers per operation and cacheTags for invalidation.
- Very specific: line 34
HTTP cache + write paths: must agree on invalidation — purge cacheTags on every write.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
API Platform 4.3 — Performance
Use when
- The API is slower than expected, especially on list endpoints.
- A
max_joins: 30exception fires because the entity has many relations. - Memory blows up on large collections.
- p99 latency is above ~50 ms for simple routes.
- Cache invalidation cannot keep up with writes.
Default workflow
- Detect the
force_eager: truetrap. Switch tofalseand replace with targeted join fetches in repositories. - Profile with the Symfony profiler in dev — look for N+1 patterns.
- For list views: project a DTO via DQL
NEW App\Dto\PostListItem(...)instead of hydrating the full entity. - Enable APCu for resource metadata cache in prod.
- Adopt FrankenPHP worker mode for high-traffic APIs (stateful kernel between requests).
- Add HTTP cache headers per operation and
cacheTagsfor invalidation.
Guardrails
force_eager: trueis the 4.x default but rarely what you want for an entity with many relations. Set it tofalseand use targetedaddSelect()join fetches per query.#[ApiProperty(fetchEager: true)]for specific fields only — never blanket eager loading.- Never iterate without
clear()on million-row collections — memory will explode. - HTTP cache + write paths must agree on invalidation — purge
cacheTagson every write. - FrankenPHP worker mode requires worker-safe code (no static state leaks).
Progressive disclosure
SKILL.mdcovers posture and rules.reference.mdcarries the full patterns: fetch modes, eager loading trap, join fetch recipe, DTO projection, batch iteration, HINT_READ_ONLY, APCu metadata cache, FrankenPHP worker, HTTP cache headers, cache tags + Varnish PURGE, dynamic Last-Modified listener.
Output contract
eager_loading.force_eager: falseconfirmed (or documented exception).- Repository methods using targeted
addSelect()for list endpoints. - DTO projections via DQL
NEWfor large lists. - APCu metadata cache configured in prod.
- HTTP cache configured per operation (where applicable).
cacheTagspurged on writes.- p99 latency measured before/after.
References
reference.md
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

