Askr documentation
Server & APIs

OpenAPI

OpenAPI at the Askr server boundary, with validated input and explicit failure states.

Example

Generate the OpenAPI artifact from executable route schemas, check it for drift in CI, then generate the typed client from that checked artifact.

askr openapi --output ./openapi.yaml
askr openapi --check --output ./openapi.yaml
askr generate ./openapi.yaml --output ./generated/api.ts

Authoring operations

createApi<Dependencies>({ info, securitySchemes, servers, validateResponses }) from @askrjs/server/openapi returns an ApiDefinition you register routes on with .get(), .post(), and the rest, chaining .pathParam(), .queryParam(), .jsonBody(), .ok(), .notFound(), and similar calls to attach both the handler and its documented shape in the same fluent statement. .access(requirement, security) attaches an AuthRequirement from @askrjs/auth alongside the OpenAPI security requirement it maps to, so authorization and documentation stay one declaration instead of two.

jsonSchema contract

api.schema("Name", schema.object({...})) registers a reusable named component: its jsonSchema is projected into the document's components.schemas section, while the original @askrjs/schema parser keeps validating request and response bodies against it at runtime. Reusing the same registered schema across multiple routes keeps the generated document's component references consistent instead of duplicating inline schemas.

Generate and check

api.toOpenApiDocument() builds a deterministic, deeply frozen OpenAPI 3.1.2 document. Duplicate operationIds, mismatched path parameters, wildcards, and unresolved schema/security references always fail the call outright; missing operationIds and undocumented responses are only rejected when you set metadata: 'authored' on createApi() — by default those two are auto-inferred/defaulted instead of enforced, so don't rely on toOpenApiDocument() to catch them unless you've opted into authored mode. The askr openapi CLI command (askr openapi --entry src/api.ts --output ./openapi.yml) writes that document as YAML, and askr openapi --check performs no writes and exits unsuccessfully if the file is missing or differs by even one byte.

Client generation

There are two paths to a typed client, and they're not the same thing: askr generate <openapi.yml> -o <dir> (from @askrjs/cli) reads an OpenAPI document and code-generates a defineApi()/createClient() client file directly, which is the fastest way to consume an API you don't own or didn't build with createApi(). For an API you do own, hand-writing the defineApi() descriptors to mirror the routes registered with createApi() is the alternative — both sides of the contract get independent, statically-checked types without depending on generated output staying in sync. The OpenAPI YAML that askr openapi produces is also useful as a portable contract for external tooling outside the Askr toolchain.