OpenAPI Documentation
VextJS has built-in automatic generation of OpenAPI documentation. Based on route validate and docs configuration, the framework generates an OpenAPI 3.0 JSON document and serves the default /docs page with the Vext Docs Renderer. Third-party documentation tools should consume /openapi.json directly.
Quick Start
1. Enable OpenAPI
Enable openapi.enabled in the configuration:
2. Add document information to the route
3. Access documents
After starting the project, visit the following address:
The default Vext Docs UI keeps HTTP API, Pages, Services, Utils, Models, discovered Components, Plugins, and Middlewares as top-level sections. Active top-level sections can collapse and expand their current navigation tree.
The HTTP API and Pages sections:
- use recursive navigation generated from OpenAPI path segments;
- keep stable resource segments such as
/api/v1/infoas categories; - use each operation
summaryas the concrete leaf label with endpoint fallback; - weaken dynamic path parameters such as
{id}instead of treating them as normal business categories.
The operation view shows response status codes as horizontal tabs without repeated panel headings, expands local schema $ref values into real fields, and avoids artificial root rows for object schemas.
The desktop sidebar stays sticky while the content scrolls, can auto-size to visible navigation labels, supports persisted manual resizing, and built-in docs assets are version-tagged so browser cache does not hide renderer updates.
The header separates search, UI controls, filters, and Authorize into clear rows, while the Overview workspace shows counts plus package startup/build/verification commands. Right-side API/code/model/plugin/middleware entries use a separated item shell so long pages remain scannable.
Pages are detected from route handlers that call res.render() or res.renderError(). Services / Utils / Components are generated from standard JSDoc without importing user code.
Models are listed from recognizable model files even when no JSDoc is present; root-level models are shown directly under Models instead of under an artificial root group, and nested model files are grouped by source directory.
The renderer statically reads supported model definition shapes to show registry key, name, collection, connection, schema fields, enums, options, indexes, methods, hooks, and usage without importing or executing model code.
Plugins and middlewares are scanned from src/plugins and src/middlewares to show JSDoc, lifecycle/bootstrap metadata, app extensions, middleware type, route usage, and source links when they can be inferred from source text.
Locales, Config, Styles, and Preload static source docs are optional advanced sources that can be enabled explicitly through openapi.docs.code.*; they are not part of the default top-level documentation surface.
On local loopback docs pages, code docs entries can include an Open source link that redirects to vscode://file/...; the link is hidden for non-local access.
Route-level docs.tags is deprecated and ignored with a warning; operation tags are inferred automatically from route path/source and are tucked into collapsed Metadata instead of being shown as primary badges.
x-tagGroups is emitted only when openapi.tagGroups is explicitly configured as a raw OpenAPI vendor extension; the built-in docs navigation does not depend on it. When OpenAPI security schemes are present, the UI shows operation security badges and a global Authorize control that is merged into same-origin Try it out requests.
B26 adds built-in theme and density controls, a more useful Overview workspace, keyboard search shortcuts, category search filters, highlighted matches, desktop page outline, copy buttons for endpoints, links, responses, usage snippets, and source paths, plus deep links for navigation leaves. Middle dynamic path parameters remain visually weak but are preserved when they lead to stable child resources, so paths such as /docs-nav/{id}/sdfs/sdfaf keep their resource hierarchy.
B27 upgrades Try it out into a lightweight request console. Each operation can show a server selector and full URL preview with Copy URL, plus tabs for Params, Headers, Body, Samples, History, and Response. Structured query/header rows stay compact when empty and still support raw fallbacks; header rows are generated from OpenAPI parameters[in=header], including validate.header. The Headers tab also shows auth status and an effective header preview so Authorize-injected headers and manual overrides are visible in one place. The Samples tab includes cURL, browser fetch, Node fetch, and Axios snippets, while the fixed Response tab keeps pretty/raw body modes and shows the actual request headers beside the response headers so you can verify what was sent. The Axios snippet is only text; Vext does not add Axios as a runtime dependency.
B31 improves the default docs page on small screens and large API surfaces. Mobile uses an off-canvas navigation drawer with synchronized search and category filters, generated field tables switch to labeled row cards under narrow breakpoints, Try it out internals are created only when an operation console is opened, and long HTTP API lists render incrementally with a Load more control while preserving deep-link targets.
B32 adds source-aware documentation surfaces for multi-version projects. If the generated OpenAPI paths contain at least two versioned source groups such as /api/v1/**, /api/v2/**, /api/beta/**, /v1/**, /v2/**, or /beta/**, Vext Docs automatically exposes an ordered All / API v1 / API v2 / API Beta style selector. Numbered versions are listed before named release channels such as alpha, beta, or rc.
Each source fetches filtered /_vext/docs/openapi.json?source=<id>, code.json?source=<id>, and search.json?source=<id> data, so the current source has its own Overview counts, navigation tree, search state, access-filtered operations, and deep links.
Non-All sources return only OpenAPI entries by default; Code JSDoc items appear for a source only when that source explicitly configures code.include / code.exclude. Existing single-source #anchor links remain valid; multi-source links use #source=<id>&view=<view>&id=<anchor>.
Projects can define custom source surfaces with openapi.docs.sources when automatic version detection is not enough. source.access, including source.access.visible, is applied to the source selector and source-aware endpoints. Every explicit source still needs a match pattern because it scopes OpenAPI data. For a code-only source, use a stable non-API namespace such as /sdk/** and opt into Code JSDoc with code.include / code.exclude.
B32 also extends Try it out for real project environments. OpenAPI servers[].variables are rendered as controls beside the server selector and are resolved into the URL preview, Copy URL, samples, history, and Send request.
Projects can optionally configure a browser-side request hook with openapi.docs.tryItOut.hookScript and hookGlobal. hookGlobal is only the lookup name, so hook notes are shown only when a hook script is configured or the runtime global exposes beforeRequest / afterResponse.
The docs page calls those hook functions around fetch, merges returned request headers/body/URL changes, and shows diagnostics in the Response tab. Hooks run only in the browser documentation page and Vext does not import or execute backend project code for them.
Multi-source configuration
Use openapi.docs.sources when Public/Admin/Internal, version, or audience boundaries cannot be inferred from the path alone:
source.access is passed to openapi.docs.access.resolver as a kind: "source" descriptor. source.access.visible: false hides a source before resolver execution. options.docs.access is emitted as x-vext-docs-access and passed to the same resolver as the access field of a kind: "operation" descriptor; visible: false hides the operation directly, and tryItOut: false disables Try it out for that operation. source.code.include / source.code.exclude opt a non-All source into Code JSDoc items; without them, non-All sources only expose OpenAPI entries. Code filters match each item's id, title, and source file, so path-like patterns such as models/* and services/sdk/** can be used for common source-file scopes.
Try it out request hook
hookScript points to a browser script loaded by the docs page. The script should expose window[hookGlobal] and may implement beforeRequest / afterResponse:
If you need to append organization-level extension fields after generation, you can use OpenAPI hooks. OpenAPIGenerator.generate() remains synchronized, and openapi:afterGenerate must also return patches synchronously:
Document configuration
Global configuration
Configure OpenAPI global information in config/default.ts:
apiKey schemes may use in: "cookie", and validate.cookie is rendered as OpenAPI cookie parameters. Browser Try it out cannot set the forbidden Cookie header directly; use same-origin browser cookies or an HTTP client such as cURL when you need manual cookie values.
Code docs scan src/services, src/utils, the configured models directory, src/frontend/components, src/plugins, and src/middlewares without importing or executing user code. Services, utils, and components require standard JSDoc on exported symbols. Models are listed from recognizable model files even without JSDoc, and JSDoc above the default export enriches the generated entry. Supported model definitions also expose schema fields, enums, options, indexes, methods, hooks, and a usage snippet in the default UI. Plugins expose inferred plugin name, dependencies, lifecycle hooks, global middleware registration, app extensions, and setup usage. Middlewares expose inferred middleware/factory type and route usage snippets. When vext start runs built output, Vext prefers <project>/src if source files are present so top-level JSDoc and local source links are preserved; if the source tree is not deployed, it falls back to the runtime directory.
Routing level document configuration
Each route can configure its OpenAPI documentation information through options.docs:
x-rate-limit is generated automatically only when the rate-limit object middleware provides positive numeric max and window options. String middleware references, missing options, partial options, and malformed option values do not emit an empty x-rate-limit, so OpenAPI consumers are not given a misleading rate-limit contract.
docs Configuration details
summary — interface summary
One sentence describing the interface function, displayed in the interface list of the document UI:
description — Detailed description
Detailed description of support for Markdown format is displayed when the interface is expanded:
tags — deprecated operation tags
Route-level docs.tags is deprecated and ignored. Vext now infers one operation tag automatically from the route path, falling back to the source file only when needed:
If an existing route still sets docs.tags, Vext ignores that value and logs a deprecation warning. Remove the field from route definitions and rely on automatic path/source inference.
operationId — operation identification
Globally unique operation identifier. If not specified, the framework automatically infers:
operationId must remain unique across the whole OpenAPI document. During generation, Vext validates both explicit docs.operationId values and inferred values: duplicate explicit values, explicit values that collide with inferred values, or different routes that infer the same value all fail generation. Fix the conflict by setting a unique docs.operationId on one route or by changing the route method/path so inferred values differ.
hidden — hide route
Routes you don't want to appear in the document (such as internal interfaces):
deprecated — Mark deprecated
Mark the interface as deprecated, and there will be a strikethrough and deprecation prompt in the document:
security — security solution
For new applications, declare route protection with RouteOptions.auth. OpenAPI security is generated from auth before the legacy middleware-name fallback:
Use auth: { security: "bearerAuth" } when you want to choose the security scheme explicitly. auth: { required: false } without roles, scopes, permissions, or check marks the route as public in OpenAPI; if those authorization rules are present, runtime still requires authentication and OpenAPI emits authentication security. config.openapi.guardSecurityMap is still supported for legacy middleware-only routes, but it should not be the primary source for new Auth examples.
Manual override:
responses — response definitions
Response document for custom routes. The key is the HTTP status code:
Response schema uses the same DSL syntax as validate, automatically converted to JSON Schema.
Response example
Multiple response example
Custom Content-Type
Response header
Automatic linkage between validate and document
The validate rule in the route is automatically mapped to the OpenAPI document, no need to write it again:
Automatically generated OpenAPI parameters:
Rules for validate.body are automatically mapped to requestBody (JSON schema):
Generated requestBody schema:
Field-level business descriptions use the explicit side-effect-free builder, and the generator outputs them as OpenAPI schema description values:
The generated requestBody schema will contain:
File upload routing (multipart/form-data)
Use RouteOptions.multipart.files to declare the file upload route, and the generator automatically outputs multipart/form-data requestBody.
Generated OpenAPI snippet:
required: true is a runtime contract as well as an OpenAPI hint. If the request omits a required upload field, Vext responds with 400 and the missing field names. Optional and undeclared file fields are accepted unless they violate maxFiles, maxFileSize, or allowedMimeTypes.
multipart.files and validate.body are mutually exclusive. When configured at the same time, multipart.files takes precedence.
Control by environment
It is recommended to enable documentation in the development environment and turn it off in the production environment:
If your production environment requires keeping API documentation (read-only reference):
visibility-only keeps the public /openapi.json complete while the Vext Docs page, docs OpenAPI/config source data, code docs, search data, and menu receive visibility-filtered data. Use enforce when hidden operations or code docs must also be removed from canonical docs data.
Custom document path
Modify the registration paths of the two endpoints through docs.path and jsonPath. docsPath remains as a compatibility field, but new projects should prefer docs.path:
Reverse proxy path prefix scenario
When an application is deployed on a reverse proxy, it needs to be handled in two situations depending on whether the proxy strips the prefix.
Case 1: Proxy stripping prefix (proxy_pass with / at the end)
At this time, the request path received by vext has removed /admin, and route registration does not need to change. The built-in docs page fetches source-aware data from /_vext/docs/*.json, so those browser-facing asset/data URLs also need the public /admin prefix. jsonPublicPath is still useful as the public canonical OpenAPI URL for external tools and metadata, but it is not the primary data endpoint used by the built-in source-aware docs UI.
Use docs.assetsPublicPath for browser-facing docs assets/data, while keeping docs.assetsPath as the internal route prefix registered by vext:
Request link:
Scenario 2: Proxy transparent transmission prefix (proxy_pass does not have / at the end)
At this time, the request path received by vext still contains /admin, and the endpoint paths need to be configured synchronously. There is no need to configure assetsPublicPath or jsonPublicPath because the browser-facing and internal paths are the same:
Comparison of two situations
servers — document interaction address
servers is a metadata field written to the OpenAPI specification document itself, independent of the endpoint registration path. Documentation UIs and third-party tools can use it as the base address for interactive requests.
Default behavior (when not configured):
The relative path / will automatically follow the domain name of the current page, and the default value is sufficient in most cases.
Scenarios that require explicit configuration:
- The documentation page and the API are not in the same domain (cross-domain)
- You want documentation UIs or third-party tools to expose environment switching
After configuration, documentation UIs or tools that support servers can let users switch the target environment.
Vext Docs uses these servers[] entries as the Try it out server list and defaults to the first valid server. For fixed local or deployed endpoints, configure the complete URL including the port, such as http://127.0.0.1:3000. Reserve servers[].variables for genuinely variable parts such as environment names, regions, tenants, or API versions; when present, they are rendered as editable controls and applied to URL preview, Copy URL, code samples, and Send requests. The Same origin option is shown automatically only when no OpenAPI servers are configured; set openapi.docs.tryItOut.sameOrigin to true or false to force the behavior. Set openapi.docs.tryItOut.defaultServer to "first", "same-origin", "custom", or an exact server URL to choose the initial selection. openapi.docs.tryItOut.customServer defaults to true, so users can temporarily target another environment from the browser without changing project config.
Import external OpenAPI
The default Vext Docs page focuses on the OpenAPI document generated by the current application. To aggregate multiple external OpenAPI documents, use an external documentation platform or standalone UI outside Vext and point it at each service's /openapi.json. Vext does not expose a third-party docs renderer hook or install documentation UI packages.
Integrate with third-party tools
Export OpenAPI specification
Visit http://localhost:3000/openapi.json to get the complete OpenAPI 3.0 JSON file, which can be used for:
- Postman — import API collection
- Insomnia — Import API workspace
- Code Generation — Use
openapi-generatorto generate client SDK - API Gateway — Import to Kong, AWS API Gateway, and more
- Documentation Platform — Import into Stoplight, ReadMe, and more
Example: Generate TypeScript client
Documentation Best Practices
1. Always provide summary
summary is the most important identifier of the interface in the document list and should be concise and clear:
2. Use consistent tags
Use Chinese or English tags uniformly, and predefine the order and description in global tags:
3. Add documentation for error responses
Common error codes should be described in responses:
4. Hide internal interfaces
Interfaces used internally by the framework or for operation and maintenance should be marked as hidden:
5. Make good use of deprecated
When iterating the API version, use deprecated instead of directly deleting the old interface:
Multi-level directory example
VextJS's file routing supports multi-level nested directories, and each level of directory is automatically mapped to a URL path segment. The default Vext Docs page uses those OpenAPI path segments to build recursive API navigation, keeps stable resource-level segments as categories, and shows concrete operation leaves under that directory. Operation leaves prefer docs.summary; if no summary is configured, they fall back to the endpoint path. Dynamic path parameters such as {id} are treated as parameters rather than normal business directories. Operation tags are inferred automatically from the route path/source and shown as lightweight metadata badges; explicit x-tagGroups remains available only as vendor extension metadata.
Directory structure
Path mapping comparison
Global tags description
Predefine global tags only when you want to add descriptions for automatically inferred operation tags. Path segments remain the default navigation source:
Each routing file
routes/api/v1/users.ts — User public interface
routes/api/v1/users/[id]/orders.ts — User orders (multi-level dynamic parameters)
routes/api/v1/admin/dashboard.ts — Management background
routes/api/v1/admin/users.ts — Management backend user management
routes/webhooks/stripe.ts — Third-party callbacks
Generated OpenAPI path
The above directory structure finally automatically generates the following OpenAPI paths. The default Vext Docs sidebar follows the path segments; tags remain operation metadata:
- Use directory hierarchy to express URL structure:
api/v1/admin/is automatically mapped to/api/v1/admin/prefix, no manual splicing is required - Dynamic parameters use the
[param]directory:users/[id]/orders.tsautomatically becomes/users/:id/orders, and theparamverification in the file will appear in the OpenAPI document - automatic operation tags: route-level
docs.tagsis deprecated and ignored; Vext infers operation tags from route path/source while the built-in docs page uses path segments for navigation - The file name is the route: No need for
app.group()or manual registration of routing prefixes, the directory structure is the routing structure :::
Tag groups (x-tagGroups)
The tags of the OpenAPI 3.x specification are one-dimensional flat lists and do not natively support nesting levels. When there are a large number of routes, all tags can become flat and hard to browse in a docs sidebar.
VextJS passes through x-tagGroups only when you explicitly configure openapi.tagGroups. The built-in Vext Docs renderer uses OpenAPI path segments as its primary recursive sidebar navigation, so x-tagGroups is treated as raw OpenAPI vendor extension metadata rather than a Vext Docs navigation feature.
Default behavior
VextJS does not generate x-tagGroups by default. The built-in Vext Docs renderer uses OpenAPI path segments as the source of truth for recursive navigation, so automatic tag grouping is unnecessary and can create misleading groups such as General / Admin.
Route-level docs.tags is deprecated and ignored. If another OpenAPI tool in your delivery chain needs x-tagGroups, explicitly specify tagGroups in the configuration and make sure the names match the automatically inferred operation tags or the global openapi.tags entries:
:::warning
tagGroups is emitted only when configured. Make sure every grouped tag name matches an operation tag or a global tags entry expected by the tool that consumes the OpenAPI document.
Effect comparison
Compatibility with hot reload
In dev mode, soft reload automatically regenerates the OpenAPI spec. If openapi.tagGroups is configured, the explicit x-tagGroups block is emitted again:
- Routing file changes → trigger hot reload
- Create a new adapter instance
- Reload routing + collect routing meta information
- Regenerate OpenAPI spec (including explicit
x-tagGroups, when configured) - Re-register the
/docsand/openapi.jsonendpoints on the new adapter
No need to restart the dev server, refresh the document page to see the updated grouping.
Complete example
Next step
- Understand how the DSL syntax of Parameter Validation maps to OpenAPI
- Learn the complete options of OpenAPI in Configuration
- See Adapter Architecture to understand the document behavior under different Adapters
- Discover how Testing verifies the accuracy of API documentation