Files
pictureFrame/.claude/skills/oro-api-dev/SKILL.md
T
football2801 a536baabd6 feat: initial commit — BMAD tooling, Claude memories, firmware scaffold
Adds the complete project foundation:
- BMAD BMM workflow tooling (_bmad/)
- Claude slash commands, skills, and project memories (.claude/)
- ESP32 firmware scaffold (PlatformIO + Waveshare e-ink driver)
- .gitignore excluding _bmad-output/ and .pio/ build artifacts

Planning artifacts (PRD, architecture, epics) are intentionally not
tracked — they live in _bmad-output/ per project convention.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-27 15:38:46 -04:00

10 KiB

name, description
name description
oro-api-dev OroCommerce API development reference covering JSON:API configuration, entity exposure, processors, filters, actions, authentication, CORS, batch API, storefront API, and testing. Use when configuring API resources, creating custom API processors, exposing entities via REST, building API integrations, or testing API endpoints in OroCommerce.

OroCommerce API Developer

Reference for API development on OroCommerce. Docs: https://doc.oroinc.com/backend/api/

Overview

Oro API implements REST conforming to JSON:API specification. Built on OroApiBundle using ChainProcessor, EntitySerializer, and Symfony Forms.

Base URL: https://{host}/api/{resource} Sandbox: https://{host}/api/doc

By default, only custom entities, dictionaries, and enumerations are accessible. Other entities must be explicitly enabled.

Enabling an Entity

Create Resources/config/oro/api.yml:

api:
    entities:
        Acme\Bundle\ExampleBundle\Entity\Example: ~

Then run:

php bin/console oro:api:cache:clear
php bin/console oro:api:doc:cache:clear   # rebuild sandbox docs

Configuration Reference

api:
    entities:
        Acme\Bundle\ExampleBundle\Entity\Example:
            # Exclude entity entirely
            exclude: false

            # Field configuration
            fields:
                id: ~
                name:
                    description: "Human-readable name"
                secretField:
                    exclude: true                # hide from API
                renamedField:
                    property_path: original_name # map API name to entity property
                computedField:
                    data_type: string
                    property_path: _             # custom processor handles this

            # Filter configuration
            filters:
                fields:
                    name: ~
                    status:
                        allow_array: true        # ?filter[status][]=a&filter[status][]=b
                    createdAt:
                        exclude: false

            # Sorter configuration
            sorters:
                fields:
                    name: ~
                    createdAt: ~

            # Action configuration
            actions:
                get: true
                get_list: true
                create: true
                update: true
                delete: false                    # disable delete
                delete_list: false               # disable bulk delete

            # Subresources
            subresources:
                relatedItems:
                    actions:
                        get_subresource:
                            exclude: false

Actions

Standard CRUD actions available per entity:

Action HTTP URL Description
get GET /api/{resource}/{id} Single resource
get_list GET /api/{resource} Collection
create POST /api/{resource} Create
update PATCH /api/{resource}/{id} Update
delete DELETE /api/{resource}/{id} Delete
delete_list DELETE /api/{resource} Bulk delete
get_subresource GET /api/{resource}/{id}/{sub} Subresource
get_relationship GET /api/{resource}/{id}/relationships/{rel} Relationship
update_relationship PATCH /api/{resource}/{id}/relationships/{rel} Set relationship
add_relationship POST /api/{resource}/{id}/relationships/{rel} Add to relationship
delete_relationship DELETE /api/{resource}/{id}/relationships/{rel} Remove from relationship

Adding Filters to Existing Oro Entities

Expose custom fields on Oro core entities as API filters without modifying core code:

api:
    entities:
        Oro\Bundle\CustomerBundle\Entity\CustomerUser:
            filters:
                fields:
                    erp_contact_id:
                        data_type: integer
                        property_path: erp_contact_id
        Oro\Bundle\CustomerBundle\Entity\Customer:
            filters:
                fields:
                    erp_customer_id:
                        data_type: integer
                        property_path: erp_customer_id

This lets API consumers query: GET /api/customerusers?filter[erp_contact_id]=12345

The property_path must match an extended field added via migration (oro_options).

Custom Processors

Processors modify request/response handling in the API pipeline. Each action has a chain of processors executed in order.

namespace Acme\Bundle\ExampleBundle\Api\Processor;

use Oro\Bundle\ApiBundle\Processor\CustomizeLoadedData\CustomizeLoadedDataContext;
use Oro\Component\ChainProcessor\ContextInterface;
use Oro\Component\ChainProcessor\ProcessorInterface;

class ComputeExampleField implements ProcessorInterface
{
    public function process(ContextInterface $context): void
    {
        /** @var CustomizeLoadedDataContext $context */
        $data = $context->getData();
        // modify $data
        $data['computedField'] = 'computed_value';
        $context->setData($data);
    }
}

Register with tag:

services:
    acme.api.processor.compute_example_field:
        class: Acme\Bundle\ExampleBundle\Api\Processor\ComputeExampleField
        tags:
            - name: oro.api.processor
              action: customize_loaded_data
              class: Acme\Bundle\ExampleBundle\Entity\Example

Processor Tag Attributes

Attribute Purpose Example Values
action Which API action to hook get, get_list, create, update, delete, customize_loaded_data
group Pipeline stage within the action initialize, normalize_input, security_check, transform_data, normalize_result
priority Execution order (higher = earlier) -10 (after defaults), 250 (early)
class Entity class to scope processor to Oro\Bundle\CustomerBundle\Entity\CustomerUser

Intercepting Create/Update with FormContext

Use FormContext to modify request data during create/update (e.g., resolve a custom field to a relationship):

use Oro\Bundle\ApiBundle\Model\Error;
use Oro\Bundle\ApiBundle\Processor\FormContext;
use Oro\Bundle\ApiBundle\Request\Constraint;
use Oro\Component\ChainProcessor\ContextInterface;
use Oro\Component\ChainProcessor\ProcessorInterface;

class ResolveCustomerFromErpId implements ProcessorInterface
{
    public function __construct(private ManagerRegistry $registry) {}

    public function process(ContextInterface|FormContext $context): void
    {
        $requestData = $context->getRequestData();
        $erpId = $requestData['erp_customer_id'] ?? null;

        if ($erpId === null) {
            return;
        }

        $customer = $this->registry
            ->getRepository(Customer::class)
            ->findOneBy(['erp_customer_id' => $erpId]);

        if ($customer !== null) {
            $requestData['customer'] = [
                'class' => Customer::class,
                'id'    => $customer->getId()
            ];
            $context->setRequestData($requestData);
        }
    }
}

Register on the create action in transform_data group:

services:
    acme.api.processor.resolve_customer:
        class: Acme\Bundle\ExampleBundle\Api\Processor\ResolveCustomerFromErpId
        arguments: ['@doctrine']
        tags:
            - { name: oro.api.processor, action: create, group: transform_data, priority: -40, class: Oro\Bundle\CustomerBundle\Entity\CustomerUser }

Authentication

API supports OAuth 2.0:

# Get token
curl -X POST https://host/oauth2-token \
  -d "grant_type=client_credentials" \
  -d "client_id=<id>" \
  -d "client_secret=<secret>"

# Use token
curl -H "Authorization: Bearer <token>" \
  -H "Accept: application/vnd.api+json" \
  https://host/api/examples

Configure OAuth apps in admin: System > User Management > OAuth Applications.

Filters

GET /api/examples?filter[name]=test
GET /api/examples?filter[status][]=active&filter[status][]=pending
GET /api/examples?filter[createdAt]>2024-01-01
GET /api/examples?page[number]=2&page[size]=10
GET /api/examples?sort=-createdAt,name
GET /api/examples?include=relatedEntity
GET /api/examples?fields[examples]=name,status

Batch API

For bulk operations:

POST /api/examples/batch
Content-Type: application/vnd.api+json

{ "data": [ { "type": "examples", "attributes": { ... } }, ... ] }

Async batch API processes large datasets via MQ. Docs: https://doc.oroinc.com/api/batch-api/

Storefront API

Separate API for storefront (customer-facing). Configure in Resources/config/oro/api_frontend.yml:

api:
    entities:
        Acme\Bundle\ExampleBundle\Entity\Example:
            # storefront-specific config

Different authentication (customer user tokens). Docs: https://doc.oroinc.com/backend/api/storefront/

CORS Configuration

# config/config.yml
oro_api:
    cors:
        preflight_max_age: 600
        allow_origins:
            - 'https://frontend.example.com'
        allow_headers:
            - 'Content-Type'
            - 'Authorization'
        expose_headers:
            - 'X-Include'
        allow_credentials: true

Testing API

Oro provides base test cases for API functional tests:

use Oro\Bundle\ApiBundle\Tests\Functional\RestJsonApiTestCase;

class ExampleApiTest extends RestJsonApiTestCase
{
    public function testGetList(): void
    {
        $response = $this->cget(['entity' => 'examples']);
        $this->assertResponseContains('expected_response.yml', $response);
    }

    public function testCreate(): void
    {
        $response = $this->post(['entity' => 'examples'], 'create_request.yml');
        $this->assertResponseContains('create_response.yml', $response);
    }
}

Test fixtures in Tests/Functional/Api/DataFixtures/. Expected responses in Tests/Functional/Api/responses/.

CLI Commands

php bin/console oro:api:cache:clear                # clear API config cache
php bin/console oro:api:doc:cache:clear             # rebuild sandbox docs
php bin/console oro:api:config:dump-reference        # dump config structure
php bin/console oro:api:debug                        # list available API resources
php bin/console oro:api:debug --sub-resources Example # show entity subresources