Перевод страницы с английского оригинала. При расхождениях приоритет имеет английская версия.
Спецификация OpenAPI в DaloyJS
DaloyJS генерирует чистый документ OpenAPI 3.1 прямо из определений маршрутов, без плагинов и без отдельных декораторов. Валидация, типы и спецификация используют один источник истины.
Одна строка: автоматический /docs, /openapi.json, /openapi.yaml
Как в FastAPI. Передайте docs: "auto" (или docs: true) в конструктор App, и DaloyJS сам зарегистрирует GET /openapi.json + GET /openapi.yaml (живую спецификацию в обоих форматах) и GET /docs (справочник API в интерфейсе Scalar).
docs: "auto" монтирует маршруты, только когда App не в продакшене (шаблоны create-daloy используют это по умолчанию, чтобы задеплоенный API не публиковал свою схему). Либо не задавайте опцию вовсе (это значение по умолчанию) и смонтируйте маршруты вручную с помощью хелперов ниже. Пути, интерфейс и теги настраиваются через объектную форму. Начиная с 1.5.5 объектная форма ведёт себя как "auto", если не задан enabled, поэтому настройка интерфейса никогда случайно не опубликует карту маршрутов в продакшене; задайте enabled: true (или docs: true) для публичного API, который должен отдавать документацию в продакшене:
Безопасность: опубликованная спецификация представляет собой полную карту поверхности атаки: каждый путь, параметр, схема аутентификации и форма ошибки. Сdocs: trueилиenabled: trueв продакшене DaloyJS пишет в логdocs.public_in_productionи отдаёт спецификацию любому, кто может до неё достучаться. Для публичного API это правильно. Во всех остальных случаях оставьте"auto"или закройте документацию аутентификацией (bearerAuth()на уровне приложения безexcept()для/docsи/openapi.jsonпокрывает их). Помечайте административные, cron- и отладочные маршруты какinternal: true. Тогда они не попадают в сгенерированный документ и отвечают404на сетевой трафик, аapp.inject()по-прежнему может их вызывать. Смотрите Secure admin panels (на английском).
Выберите интерфейс: Scalar, Swagger UI или Redoc
Задайте ui значением "scalar" (по умолчанию), "swagger" или "redoc". Все три отображают одну и ту же живую спецификацию, монтируются на те же пути и поставляются с той же строгой CSP и ресурсами с CDN, так что переключение сводится к замене одного слова. Scalar и Swagger UI включают консоль запросов для разработчиков, работающую с защищёнными маршрутами. Redoc является справочником только для чтения: он показывает требования безопасности, но встроенной консоли Try it у него нет.
Swagger UI по умолчанию сохраняет авторизации из диалога Authorize между перезагрузками. Для этого он хранит вставленный токен в localStorage браузера, как и Scalar с persistAuth: true. Любой скрипт, выполняющийся на origin документации, может его прочитать, и он переживает закрытие вкладки. Вставляйте только короткоживущие тестовые токены с минимальными правами, никогда не используйте продакшен- или административные учётные данные, и отдавайте документацию с origin, отдельного от любого собственного веб-приложения. Передайте swagger: { persistAuthorization: false }, чтобы хранить токены только в памяти. Scalar автоматически выбирает первую настроенную схему безопасности OpenAPI, поэтому форма аутентификации сразу готова к тому, чтобы разработчик вставил bearer-токен или API-ключ. Если вам нужно другое значение по умолчанию в Scalar, задайте его явно:
Если в документации нужна видимая точка входа для входа в систему, добавьте docs.auth. Одна и та же кнопка отображается в Scalar, Swagger UI и Redoc. Направьте её на локальную форму входа, маршрут Better Auth, URL входа Clerk или URL авторизации OIDC от Auth0, Okta, Microsoft Entra ID, Cognito, Keycloak или другого провайдера:
Объект redoc передаётся как есть в Redoc.init(specUrl, configuration, element). Поскольку Redoc строит поисковый индекс в Web Worker из blob:, автоматически смонтированный маршрут /docs расширяет CSP этой страницы директивой worker-src 'self' blob: только для ui: "redoc". Scalar и Swagger UI сохраняют более строгую политику. Опция scalar игнорируется, если ui не равен "scalar", и то же самое относится к swagger и redoc.
Объект scalar передаётся в HTML API Scalar как JSON-конфигурация, а Daloy сохраняет живой openapiPath в качестве источника. Используйте его для тем, собственного CSS, раскладки, настроек аутентификации по умолчанию и видимости клиентов, не копируя HTML документации.
Для продвинутых: генерация спецификации вручную
Нужен «сырой» объект спецификации (для кодогенерации, контрактных тестов или собственного маршрута)? Вызовите generateOpenAPI(app, options) напрямую:
Для продвинутых: документация на собственном маршруте
Написанный вручную маршрут документации является обычным маршрутом, поэтому ограничение docs: "auto" на него не распространяется. Сами решите, должен ли он существовать в продакшене и нужна ли ему аутентификация. То же относится к любому маршруту /openapi.json, который вы отдаёте вручную через generateOpenAPI():
swaggerUiHtml, scalarHtml и redocHtml возвращают самодостаточные HTML-страницы, которые загружают ресурсы с jsDelivr при строгой CSP, разрешающей только этот origin. Если вы пишете маршрут вручную с redocHtml, передайте allowBlobWorkers: true в htmlResponse (или в docsContentSecurityPolicy), чтобы CSP разрешила поисковый worker Redoc из blob::
Если хотите проверить удобство документации на гораздо большем контракте, посмотрите большое демо фейкового REST API (на английском). Это лучший бенчмарк, чем игрушечный CRUD-пример, когда нужно проверить поиск, группировку и скорость отрисовки.
Сохранение на диск для кодогенерации
Что попадает в документ
- Один
operationIdна маршрут, дубликаты вызывают исключение при регистрации. - Параметры пути
:idнормализуются в{id}. - Тела со схемами конвертируются через
schema.toJSONSchema?.(), если это поддерживается, иначе через структурный запасной вариант. - Переиспользуемая
components.schemas.Problemдля ошибок RFC 9457. tags,summary,descriptionиdescriptionдля каждого статуса.- Маршруты с
internal: trueпропускаются, если не передатьincludeInternal: true(например, для внутреннего контракта для ваших собственных инструментов). Никогда не публикуйте такой документ.
Вебхуки
OpenAPI 3.1 позволяет производителю публиковать вебхуки верхнего уровня: операции, которые должен реализовать потребитель. Передайте webhooks в generateOpenAPI, и DaloyJS выведет их в карте webhooks верхнего уровня документа.
Карта webhooks только описывает операцию; она ничего не доставляет и не защищает. Для настоящих доставок подписывайте каждую полезную нагрузку, чтобы получатели могли проверить и источник, и целостность, и добавляйте метку времени, чтобы они могли отклонять повторы. Один лишь статический bearer-токен не доказывает ни того, ни другого. createWebhookSender() (на английском) подписывает, ставит метку времени, повторяет попытки и пропускает каждый исходящий запрос через fetchGuard(). Получатели проверяют подпись через verifyWebhookSignature(). Описывайте заголовки подписи в description вебхука.
Колбэки
Колбэки описывают внеполосные запросы, которые операция может отправить потребителю (например, эндпоинт подписки, который позже делает POST на URL, переданный вызывающей стороной). Прикрепите карту callbacks прямо к маршруту или вебхуку.
Безопасность: URL колбэка, переданный вызывающей стороной, является приёмником для Server-Side Request Forgery. Валидации схемой недостаточно.z.url()сам по себе принимаетhttp://169.254.169.254/(метаданные облака),http://localhost:6379/и URLfile:/gopher:, и дажеz.url({ protocol: /^https$/ })всё ещё принимаетhttps://169.254.169.254/или имя хоста, которое разрешается в приватный адрес. Отправляйте каждый колбэк черезsafeFetchизfetchGuard()(на английском), который проверяет разрешённый адрес и каждый шаг перенаправления. А лучше доставляйте черезcreateWebhookSender()(на английском), который также подписывает полезную нагрузку. Подумайте о проверке владения URL, например запросом-вызовом (challenge), прежде чем его сохранять.
Каждое имя колбэка сопоставляется с одним или несколькими ключами runtime-выражений (например, "{$request.body#/callbackUrl}"), каждый из которых сопоставляется с одной или несколькими операциями по HTTP-методу. Пустые карты и пустые массивы пропускаются, поэтому пустой колбэк никогда не даёт некорректную спецификацию.
Размеченные объединения
discriminator в OpenAPI 3.1 является каноническим способом описывать размеченные (tagged) объединения. DaloyJS поставляет два хелпера из @daloyjs/core/openapi (и из корневого пакета):
discriminator(propertyName, mapping?): «голый» построитель спецификации. Используйте его, когда у вас уже есть написанная вручную JSON Schema и нужно аккуратно прикрепить к ней поле.discriminatedUnion(propertyName, variants, opts?): совместимая со Standard Schema обёртка, которая и валидирует во время выполнения (выбирая вариант по значению дискриминатора), и предоставляет.toJSONSchema(), чтобы генератор OpenAPI автоматически выводил{ oneOf, discriminator }.
Во время выполнения обёртка отклоняет не-объекты, отсутствующие или нестроковые дискриминаторы и неизвестные значения дискриминатора (включая унаследованные имена, такие как __proto__, constructor и toString) с понятной ошибкой Standard Schema, а во всём остальном полагается на валидатор подходящего варианта.
Последнее обновление: