Skip to Content
šŸŽ‰ Try out our new interactive playground
GuidesConceptsExtract inline schemas

Extract inline schemas

We have experimental support for ā€œextracting inline schemasā€ behind the --extract-inline-schemas / OPENAPI_EXTRACT_INLINE_SCHEMAS=true configuration flag.

What does this mean?

There are basically two ways you can define schemas in your openapi specifications:

  • Named schemas
  • Inline schemas

These are handled differently by code generation. Enabling --extract-inline-schemas aims to make inline schemas emit similar code to named schemas.

Named schema example

Normally when writing openapi specifications it is desirable to make use of $ref and define your schemas as named components.

paths: /list/{listId}: parameters: - $ref: '#/components/parameters/listId' get: operationId: getTodoListById responses: 200: description: 'success' content: application/json: schema: $ref: '#/components/schemas/TodoList' components: schemas: TodoList: type: object required: - id - name - totalItemCount - incompleteItemCount - created properties: id: type: string format: uuid name: type: string totalItemCount: type: number incompleteItemCount: type: number created: type: string format: date-time

When we run code generation for this, we expect a type and a schema for the TodoList to be generated, something like:

import {z} from 'zod' export type t_TodoList = { id: string name: string totalItemCount: number incompleteItemCount: number created: string } export const s_TodoList = z.object({ id: z.string(), name: z.string(), totalItemCount: z.coerce.number(), incompleteItemCount: z.coerce.number(), created: z.string().datetime({ offset: true }), })

This is useful, as it means that we can easily reference the type, or use the schema as we require.

Inline Schema Example

However, not everyone will write their specifications using named $refs, and instead inline schemas may be used. This is especially prolific when generating the specification from implementation code in our experience.

Consider the same example as above, but with the schema inlined:

paths: /list/{listId}: parameters: - $ref: '#/components/parameters/listId' get: operationId: getTodoListById responses: 200: description: 'success' content: application/json: schema: type: object required: - id - name - totalItemCount - incompleteItemCount - created properties: id: type: string format: uuid name: type: string totalItemCount: type: number incompleteItemCount: type: number created: type: string format: date-time components: schemas: {}

By default, this will be emitted as in-line types / schemas

export type GetTodoListById = ( params: Params<t_GetTodoListByIdParamSchema, void, void>, respond: GetTodoListByIdResponder, ctx: RouterContext, ) => Promise< | KoaRuntimeResponse<unknown> | Response< 200, { id: string name: string totalItemCount: number incompleteItemCount: number created: string } > | Response<StatusCode4xx, t_Error> | Response<StatusCode, void> > const getTodoListByIdResponseValidator = responseValidationFactory( [ [ "200", z.object({ id: z.string(), name: z.string(), totalItemCount: z.coerce.number(), incompleteItemCount: z.coerce.number(), created: z.string().datetime({ offset: true }), }), ], ["4XX", s_Error], ], z.undefined(), ) router.get("getTodoListById", "/list/:listId", async (ctx, next) => { // ... const responder = { with200() { return new KoaRuntimeResponse<{ id: string name: string totalItemCount: number incompleteItemCount: number created: string }>(200) } } // ... })

With --extract-inline-schemas enabled

Notice how this:

  • Creates a lot of duplication, we have to repeat the definition anytime it is used
  • Makes it inconvenient, or impossible to reference the type/schema in our implementation code

With --extract-inline-schemas enabled, the code generator will synthesis a name for each inline schema based on its usage, and emit exported types/schemas, eg:

export type t_getTodoListByIdJson200Response = { id: string name: string totalItemCount: number incompleteItemCount: number created: string } export const s_getTodoListByIdJson200Response = z.object({ id: z.string(), name: z.string(), totalItemCount: z.coerce.number(), incompleteItemCount: z.coerce.number(), created: z.string().datetime({ offset: true }), })

This can be a handy trick to make the code generated from schemas you donā€™t own/control easier to work with. In general you should prefer to improve the specifications to be more suitable for code generation, which generally also improves the result of documentation tools like Redocā€‰

Last updated on