
Oro Api
- 5 installs
- 2 repo stars
- Updated July 22, 2026
- netresearch/orocommerce-skill
Helps with backend & apis tasks.
About
oro-api is a Claude Code skill for backend & apis. It helps solo builders move faster with AI-assisted coding.
- oro-api
- Backend & APIs
- AI-coding skill
Oro Api by the numbers
- 5 all-time installs (skills.sh)
- +1 installs in the week ending Aug 4, 2026 (Skillselion tracking)
- Ranked #3,685 of 4,347 Backend & APIs skills by installs in the Skillselion catalog
- Data as of Aug 4, 2026 (Skillselion catalog sync)
npx skills add https://github.com/netresearch/orocommerce-skill --skill oro-apiAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 5 |
|---|---|
| repo stars | ★ 2 |
| Last updated | July 22, 2026 |
| Repository | netresearch/orocommerce-skill ↗ |
What it does
Helps with backend & apis tasks.
Files
OroCommerce v6.1 REST API Configuration & Development
Core File Locations
Two separate configs for two firewalls — never mix them:
- Back-Office API (
Resources/config/oro/api.yml) — Admin users, internal integrations. Full CRUD by default. - Storefront API (
Resources/config/oro/api_frontend.yml) — Customers, PWA, mobile apps. Typically restricted to read-only with a subset of properties.
Bundle auto-discovery loads both on kernel compilation. The firewall contexts differ; storefront config in api.yml is silently ignored.
Basic Entity Exposure
# Resources/config/oro/api.yml
api:
entities:
Acme\Bundle\DemoBundle\Entity\Document: ~This auto-exposes all entity properties via JSON:API endpoints (GET list/detail, POST, PATCH, DELETE).
Field Configuration
api:
entities:
Acme\Bundle\DemoBundle\Entity\Document:
fields:
internalId:
exclude: true # Never expose
displayName:
property_path: name # Rename property
createdAt:
data_type: datetime
direction: output # Read-only; clients can't modifyField directions: input (write-only), output (read-only), both (default). Use exclusion_policy: all on sensitive entities to whitelist fields explicitly.
See api-patterns.md for exclusion policies, filter/sorter config, subresources, action disabling, and a complete entity example.
Custom API Processors
Processors intercept requests and manipulate data:
namespace Acme\Bundle\DemoBundle\Api\Processor;
use Oro\Component\ChainProcessor\ContextInterface;
use Oro\Bundle\ApiBundle\Processor\ProcessorInterface;
class NormalizeDocumentData implements ProcessorInterface
{
#[\Override]
public function process(ContextInterface $context): void
{
if ($context->hasProcessed(__METHOD__)) {
return;
}
$data = $context->getResult();
// Transform data...
$context->setResult($data);
$context->setProcessed(__METHOD__);
}
}Register via service tag:
services:
acme.api.processor.normalize_document_data:
class: Acme\Bundle\DemoBundle\Api\Processor\NormalizeDocumentData
tags:
- { name: oro.api.processor, action: get, group: normalize_data, priority: 10 }Processors execute in a 10-group pipeline: initialize, resource_check, normalize_input, security_check, load_data, data_security_check, transform_data, save_data, normalize_data, finalize. Most custom logic goes into normalize_data. Higher priority values run first.
See processor-groups.md for the full group reference with per-group context and hook guidance.
Key Pitfalls
1. api_frontend.yml config ignored: Config in api.yml does not affect storefront. Storefront needs its own api_frontend.yml with separate field/action definitions. 2. Processor priority ordering: Higher priority = runs first. Review registration order when custom logic doesn't execute. 3. Cache not cleared: API caches config aggressively. Run oro:api:cache:clear and cache:clear after YAML changes. Symptoms: missing fields, broken filters, stale action states.
See Also
- api-patterns.md — Filters, sorters, subresources, actions, exclusion policies, complete entity example
- processor-groups.md — Full processor group reference and execution order
- v6.1.md — v6.1 specifics, backward compatibility, and migration notes
- v7.0.md — v7.0 changes (placeholder)
API Configuration Patterns (v6.1)
Detailed configuration examples for OroCommerce REST API development. See the main SKILL.md for core concepts and quick-start patterns.
Exclusion Policies
Control defaults for all fields:
api:
entities:
Acme\Bundle\DemoBundle\Entity\Document:
exclusion_policy: all # Exclude all by default; must whitelist
fields:
id:
exclude: false
subject:
exclude: falsePolicies:
none(default) — Include all fields; exclude listed onesall— Exclude all fields; include listed onescustom_fields— Exclude dynamic custom fields; include entity properties
Use all for sensitive entities (users, orders) to whitelist only public fields.
Filter Configuration
Allow filtering by specific fields:
api:
entities:
Acme\Bundle\DemoBundle\Entity\Document:
filters:
columns:
id:
data_name: d.id
subject:
data_name: d.subject
status:
data_name: d.status
operators: [eq, neq, in, nin]
createdAt:
data_name: d.createdAt
operators: [eq, lt, lte, gt, gte]Common operators: eq, neq, in, nin, lt, lte, gt, gte, exists, contains, starts_with.
Clients filter via query params: /api/documents?filter[subject]=Hello&filter[status]=active.
Sorters
Allow sorting by specified fields:
api:
entities:
Acme\Bundle\DemoBundle\Entity\Document:
sorters:
columns:
id:
data_name: d.id
subject:
data_name: d.subject
createdAt:
data_name: d.createdAtClients sort via query params: /api/documents?sort=-createdAt,subject (descending createdAt, ascending subject).
Subresource Configuration
Expose relationships as separate endpoints:
api:
entities:
Acme\Bundle\DemoBundle\Entity\Document:
subresources:
comments:
target_class: Acme\Bundle\DemoBundle\Entity\Comment
target_association: commentsEnables: /api/documents/{id}/comments (list document's comments).
Configure filters/sorters on subresources:
subresources:
comments:
target_class: Acme\Bundle\DemoBundle\Entity\Comment
target_association: comments
filters:
columns:
author:
data_name: c.author
sorters:
columns:
createdAt:
data_name: c.createdAtAction Configuration
Disable specific CRUD operations:
api:
entities:
Acme\Bundle\DemoBundle\Entity\Document:
actions:
delete: false # Disallow DELETE
delete_list: false # Disallow batch DELETE
create: false # Disallow POST
update: false # Disallow PATCHAll actions: get, get_list, create, update, delete, delete_list, add_subresource, delete_subresource, get_subresource, get_relationship, add_relationship, delete_relationship, update_relationship.
Complete Entity Example (v6.1)
A full example showing fields, filters, sorters, actions, subresources, and a paired storefront config:
api:
entities:
Acme\Bundle\DemoBundle\Entity\Document:
exclusion_policy: all
description: 'API endpoint for managing documents'
fields:
id:
exclude: false
subject:
exclude: false
description:
exclude: false
status:
exclude: false
priority:
exclude: false
data_type: integer
internalNotes:
exclude: true # Never expose
createdAt:
exclude: false
direction: output # Read-only
updatedAt:
exclude: false
direction: output
filters:
columns:
subject:
data_name: d.subject
status:
data_name: d.status
operators: [eq, neq, in]
priority:
data_name: d.priority
operators: [eq, lt, gt, lte, gte]
sorters:
columns:
id:
data_name: d.id
subject:
data_name: d.subject
createdAt:
data_name: d.createdAt
actions:
delete_list: false # Disallow batch delete
subresources:
comments:
target_class: Acme\Bundle\DemoBundle\Entity\Comment
target_association: comments
filters:
columns:
author:
data_name: c.author
api_frontend: # Storefront API config (separate)
entities:
Acme\Bundle\DemoBundle\Entity\Document:
exclusion_policy: all
fields:
id:
exclude: false
subject:
exclude: false
description:
exclude: false
actions:
create: false # Customers can't create documents
update: false # Customers can't edit
delete: false # Customers can't deleteAdditional Pitfalls
Excluded Fields Are Fully Hidden
exclude: true removes a field completely. Clients cannot request it, even with ?include=field. Use direction: output if clients need to view but not modify.
Cache Not Cleared After Config Changes
API caches config aggressively. After YAML changes:
php bin/console oro:api:cache:clear
php bin/console cache:clearSymptoms: New fields don't appear, filters don't work, actions disabled but still accessible.
OroCommerce v6.1 API Processor Groups Reference
Processor Group Execution Order
API processors execute in a strict pipeline. Each group runs all processors before the next group begins.
1. initialize
Purpose: Setup request context, validate route parameters, initialize data structures.
When to hook:
- Setting up shared data in context
- Validating entity exists before processing
- Initializing custom state
Example:
tags:
- { name: oro.api.processor, action: get, group: initialize, priority: 10 }Available context: Request method, entity name, resource class.
---
2. resource_check
Purpose: Verify the requested resource (entity/relationship) exists and is valid.
When to hook:
- Rarely needed; core processors handle this
- Custom resource type validation
Example:
tags:
- { name: oro.api.processor, action: get, group: resource_check, priority: 10 }---
3. normalize_input
Purpose: Parse and normalize client input (POST/PATCH bodies, query parameters).
When to hook:
- Custom input parsing before validation
- Field value transformations (e.g., string to date)
- Default value injection
Example:
public function process(Context $context) {
$data = $context->getRequestData();
if (isset($data['date_string'])) {
$data['date'] = \DateTime::createFromFormat('Y-m-d', $data['date_string']);
$context->setRequestData($data);
}
}Tag:
tags:
- { name: oro.api.processor, action: create, group: normalize_input, priority: 10 }---
4. security_check
Purpose: Check user permissions (ACL, roles) for the action.
When to hook:
- Custom permission logic beyond ACL
- Resource-level authorization (not implemented by default, use here)
- Audit logging for permission checks
Example:
public function process(Context $context) {
// Custom authorization logic
if (!$this->authorizationChecker->isGranted('EDIT', $resource)) {
$context->addError(new Error('Forbidden', '403'));
}
}Tag:
tags:
- { name: oro.api.processor, action: update, group: security_check, priority: 10 }---
5. load_data
Purpose: Fetch entity data from database.
When to hook:
- Rarely; core Doctrine processor handles this
- Custom datasources (non-Doctrine)
- Prefetching related entities
Example:
public function process(Context $context) {
$entityId = $context->getId();
$entity = $this->repository->find($entityId);
if ($entity) {
$context->setResult($entity);
}
}Tag:
tags:
- { name: oro.api.processor, action: get, group: load_data, priority: 10 }---
6. data_security_check
Purpose: Check field-level security (which fields user can access).
When to hook:
- Custom field-level authorization
- Sensitivity-based field filtering
- Cross-tenant data isolation
Example:
public function process(Context $context) {
$config = $context->getConfig();
foreach ($config->getFields() as $fieldName => $fieldConfig) {
if (!$this->fieldSecurityProvider->hasAccess($fieldName)) {
$fieldConfig->setExcluded(true);
}
}
}Tag:
tags:
- { name: oro.api.processor, action: get, group: data_security_check, priority: 10 }---
7. transform_data
Purpose: Convert entity data to API format (JSON:API serialization, transformations).
When to hook:
- Custom field formatting (e.g., number formatting)
- Denormalization (flatten nested objects)
- Data aggregation (computed fields)
Example:
public function process(Context $context) {
$data = $context->getResult();
if (is_array($data) && isset($data['amount'])) {
$data['amount_formatted'] = number_format($data['amount'], 2, '.', ',');
$context->setResult($data);
}
}Tag:
tags:
- { name: oro.api.processor, action: get, group: transform_data, priority: 10 }---
8. save_data
Purpose: Persist entity changes to database (for create/update/delete).
When to hook:
- Rarely; Doctrine processor handles persistence
- Custom persistence logic (non-database backends)
- Pre-commit validation
Example:
public function process(Context $context) {
$entity = $context->getResult();
if ($entity) {
$this->entityManager->persist($entity);
$this->entityManager->flush();
}
}Tag:
tags:
- { name: oro.api.processor, action: create, group: save_data, priority: 10 }---
9. normalize_data
Purpose: Final data transformations and normalization before output.
When to hook (most common):
- Add computed/virtual fields
- Enrich response with additional data
- Format output for client expectations
- Attach relationship counts or summaries
Example:
public function process(Context $context) {
$data = $context->getResult();
if (is_array($data)) {
$data['comment_count'] = count($data['comments'] ?? []);
$data['status_label'] = $this->getStatusLabel($data['status']);
$context->setResult($data);
}
}Tag:
tags:
- { name: oro.api.processor, action: get, group: normalize_data, priority: 10 }Most processors you write will be in this group.
---
10. finalize
Purpose: Final cleanup, logging, response finalization.
When to hook:
- Logging/auditing
- Cache invalidation
- Cleanup resources
- Adding response headers
Example:
public function process(Context $context) {
$this->logger->info('API response generated', [
'action' => $context->getAction(),
'entity' => $context->getClassName(),
]);
}Tag:
tags:
- { name: oro.api.processor, action: get, group: finalize, priority: 10 }---
Action-Specific Group Availability
Not all groups apply to every action. Common combinations:
GET (retrieve single)
- initialize → resource_check → security_check → load_data → data_security_check → transform_data → normalize_data → finalize
GET_LIST (list with filters/sorting)
- initialize → security_check → load_data → transform_data → normalize_data → finalize
- (no resource_check; no data_security_check for list context)
CREATE (POST)
- initialize → security_check → normalize_input → load_data → transform_data → save_data → normalize_data → finalize
UPDATE (PATCH)
- initialize → resource_check → security_check → normalize_input → load_data → transform_data → save_data → normalize_data → finalize
DELETE
- initialize → resource_check → security_check → load_data → save_data → finalize
---
Priority Reference
Processor execution order within a group:
# Higher priority runs FIRST
- { name: oro.api.processor, group: normalize_data, priority: 100 } # Runs first
- { name: oro.api.processor, group: normalize_data, priority: 50 } # Runs second
- { name: oro.api.processor, group: normalize_data, priority: 10 } # Runs third
- { name: oro.api.processor, group: normalize_data, priority: 0 } # Runs lastDefault core priorities:
- 255 — Pre-processing (early setup)
- 128 — Main logic
- 64 — Post-processing (cleanup)
- 0 — Final catch-all
Set your processor priority based on dependencies:
- High (100+): Runs before most core logic; useful for input validation
- Medium (50): Runs alongside core logic
- Low (10): Runs after core; safe for enhancement/enrichment
- Very Low (0): Runs last; good for cleanup/logging
---
Common Processor Patterns (v6.1)
Pattern 1: Add Computed Field (normalize_data, priority: 10)
public function process(Context $context) {
$data = $context->getResult();
if (is_array($data)) {
$data['full_name'] = $data['first_name'] . ' ' . $data['last_name'];
$context->setResult($data);
}
}Pattern 2: Enrich Response with Related Data (normalize_data, priority: 10)
public function process(Context $context) {
$entity = $context->getResult();
if ($entity instanceof Document) {
$commentCount = $this->commentRepo->countByDocument($entity->getId());
$data = $context->getResult();
$data['comment_count'] = $commentCount;
$context->setResult($data);
}
}Pattern 3: Custom Input Validation (normalize_input, priority: 100)
public function process(Context $context) {
$data = $context->getRequestData();
if (isset($data['email']) && !filter_var($data['email'], FILTER_VALIDATE_EMAIL)) {
$context->addError(new Error('Invalid email format', '400'));
}
}Pattern 4: Object-Level Authorization (data_security_check, priority: 100)
// Use data_security_check, NOT security_check — the entity is only
// available after load_data has run. security_check is for type-level
// (class) ACL checks only.
public function process(ContextInterface $context): void {
$entity = $context->getResult();
if (!$entity instanceof Document) {
return;
}
if ($entity->getOwner()?->getId() !== $this->tokenAccessor->getUserId()) {
throw new AccessDeniedException('Access denied');
}
}Service tag: { name: oro.api.processor, action: get, group: data_security_check, priority: 100 }
---
Processor Context Methods
Common context methods available in all processors:
// Getters
$action = $context->getAction(); // 'get', 'create', 'update', etc.
$className = $context->getClassName(); // Full entity class path
$config = $context->getConfig(); // API config object
$entity = $context->getResult(); // Entity (only after load_data group)
$data = $context->getRequestData(); // Client input (POST/PATCH body)
$id = $context->getId(); // Entity ID for get/update/delete
// Setters
$context->setResult($entity); // Update entity
$context->setRequestData($data); // Update input
$context->set('key', $value); // Store arbitrary data
// Error handling
$context->addError(new Error('msg', '400')); // Add error
$context->hasErrors(); // Check for errors---
Debugging Processors
Enable API processor logging:
# config/packages/dev/monolog.yml
monolog:
channels:
- oro_api
loggers:
oro.api:
level: debug
channels: [oro_api]Check logs:
tail -f var/logs/dev.log | grep "oro.api"Inspect processor execution:
php bin/console debug:container --tag=oro.api.processorLists all registered processors with groups and priorities.
REST API — v6.1 Notes
Changes from v6.0
- No breaking changes to YAML structure
- Processor priority handling clarified (higher = runs first within group)
- Filter operator consistency improved
- Subresource configuration finalized
Backward Compatibility
Supported
- YAML configuration format from v5.4+
- Processor event interfaces stable across v6.x
- JSON:API output format compliant with v5.x clients
- Entity exposure via minimal config:
Entity: ~
Deprecated (Still Works, Logs Warning)
- Inline processor registration via XML — use
services.yml+ tags instead - Custom action handlers — use processors instead
Removed
- PHP-based entity registration (deprecated in v5.4)
- HATEOAS links (deprecated in favor of JSON:API standard)
Debugging Tips
Inspect Request/Response
# Enable API channel logging in config/packages/dev/monolog.yml (oro_api channel)
tail -f var/logs/dev.log | grep "oro_api"Check Entity Config Loading
// In a console command or test
$config = $this->apiConfigProvider->getConfig(Document::class);
dump($config->toArray());Verify Processor Order
php bin/console debug:container --tag=oro.api.processor | grep "normalize_data"Performance Notes
eqfilter is fastest;containsandstarts_withslower on large datasets- Each subresource endpoint hits the database — paginate large collections
- Don't include unneeded relationship data in responses (use sparse fieldsets)
- Lower priority processors run last — useful for expensive operations
Migration Checklist: v6.0 to v6.1
1. YAML configs work as-is; no changes needed 2. Verify custom processors use stable group names 3. Re-run API integration tests; ensure endpoints respond 4. If using custom filter operators, verify they work in v6.1 5. Update API docs if endpoint behavior changed
REST API — v7.0 Notes
v7.0 is not yet released. This file will be updated when v7.0 stabilizes.
Expected Changes
- TBD