DEV Community

Cover image for A modern API reference for Symfony with Scalar
Aleksander Frolov
Aleksander Frolov

Posted on

A modern API reference for Symfony with Scalar

Symfony had no official integration with Scalar — the open-source API Reference renderer that makes Swagger UI look its age. Laravel got one back in 2024: scalar/laravel has 231,862 installs on Packagist. Symfony got nothing — not a single package on Packagist, and zero in Scalar's official list of 30+ integrations (Express, FastAPI, NestJS, Spring Boot, Laravel — but no Symfony).

I work with Symfony daily, so I closed the gap: the alex-frolov/scalar-symfony bundle.

What the bundle does

It renders Scalar API Reference from any OpenAPI document. One route, zero coupling to how the spec was generated: a static openapi.yaml, swagger-php, NelmioApiDocBundle, or API Platform all work — because the bundle never parses or proxies the document; the page loads it client-side.

Install and configure in two files:

composer require alex-frolov/scalar-symfony
Enter fullscreen mode Exit fullscreen mode
# config/packages/scalar_symfony.yaml
scalar_symfony:
    url: '/openapi.yaml'          # your OpenAPI document (required)
    path: '/scalar'               # route (default: /scalar)
    cdn: 'https://cdn.jsdelivr.net/npm/@scalar/api-reference@1.65.1'

    configuration:
        theme: 'default'
        metaData:
            title: 'API Reference'

    scalar_options:               # any Scalar option, passed through as is
        darkMode: false
        layout: 'modern'

    access_control:
        mode: public              # or 'attribute' + security attribute
Enter fullscreen mode Exit fullscreen mode

That's it — the reference lives at /scalar. The config is serialized XSS-safely (JSON_HEX_TAG/APOS/AMP/QUOT), so even a malicious title can't break out of the <script> context.

Documentation that works as a client

The bundle serves the API reference of the Tender Platform (Symfony 8.1, highload auction API, OpenAPI 3.1 spec). Through Test Request I'm hitting POST /auth/register, filling the JSON body, and pressing Send:

HTTP/1.1 201 Created  (794 ms)
{
  "company_id": "0c7702c6-9667-4ea3-8caa-df4990522ee7",
  "user_id": "86a40d70-5ef9-44fd-882b-8f703e10df7e",
  "verification_status": "pending"
}
Enter fullscreen mode Exit fullscreen mode

Real UUIDs, real latency, real backend: the docs page doubles as the tool you test the API with. That's what Swagger UI never delivered.

Quality bar

The first commit was small; the release survived a review against Symfony's official bundle best practices and external hardening passes.

  • Functional tests: 16 tests / 46 assertions — routes 200/403/404, config validation errors, XSS-escaping, real SecurityBundle integration;
  • Static analysis: PHPStan level max, 0 errors;
  • CI matrix: PHP 8.2/8.3/8.5 × Symfony 6.4/7.2/7.4/8.0, including --prefer-lowest;
  • Config hardening: attribute mode without Symfony Security fails cache:clear at compile time with a clear message;
  • Security docs: SRI (SHA-384), CSP/nonce guidance, self-hosting recipe.

The CI war story: a token that didn't fit

Half the GitHub Actions jobs failed with Your github oauth token for github.com contains invalid characters. setup-php writes the Actions GITHUB_TOKEN (prefixed ghs_) into composer's global auth.json, and Composer 2.8 only accepts ghp_/gho_/github_pat_. The fix: delete auth.json on the runner before composer validate --no-check-publish, keep the token for dependency installation.

Making it official

I opened a proposal in the Scalar organization — Discussion #9920: "Proposal: official Symfony integration (scalar/symfony)"https://github.com/scalar/scalar/discussions/9920

If you're a Symfony developer who wants modern API docs the way Laravel has them, a reaction on the discussion helps signal maintainer attention.

Start tomorrow: composer require alex-frolov/scalar-symfony → point scalar_symfony.url at any OpenAPI document → import routes and open /scalar.


Aleksander Frolov — Senior/Staff PHP engineer, Symfony/Laravel, highload. frolov.guru · GitHub

Top comments (0)