My aggregated monorepo of OCaml code, automaintained
OpenAPI Code Generator Roadmap#
This document outlines future enhancements for the OCaml OpenAPI code generator.
Recently Completed#
Union Type Generation (v0.2)#
- Generate OCaml variant types for
oneOf/anyOfschemas at the top level - Support discriminator-based codec (using
Jsont.Object.Case) - Support try-each codec for schemas without discriminators
- Currently handles schema-level unions; field-level unions fallback to
Jsont.json
Typed Error Responses (v0.2)#
- Enhanced
api_errortype withparsed_bodyfield error_bodytype:Raw,Json, orTypedvariants- Generate error parsing code for operations with typed error schemas
- Fallback to JSON/raw parsing for untyped errors
Field-Level Union Types (v0.3)#
- Detect
oneOf/anyOfin property definitions - For primitive-only unions (string|int|bool), generate polymorphic variants
- For unions with schema references, fall back to
Jsont.jsondue to module ordering constraints - Example:
oneOf: [{type: string}, {type: integer}]→[ \String of string | `Int of int ]`
Runtime Validation (v0.3)#
- Added validation functions:
validated_string,validated_int,validated_float,validated_list - String validation:
minLength,maxLength,pattern(using Re library with PCRE syntax) - Number validation:
minimum,maximum,exclusiveMinimum,exclusiveMaximum - List validation:
minItems,maxItems,uniqueItems - Validation errors reported via
Jsont.Error.msgf - Added
poly_union_decoderhelper for polymorphic variant union types
Two-Phase Module Generation (v0.4)#
- Solved the module ordering problem for union types referencing sibling schemas
- Each prefix module now uses a two-phase structure:
- Phase 1 (Types module): All type definitions, ordered by TYPE dependencies
- Phase 2 (Full modules): Full modules with
include Types.X+ codecs, ordered by CODEC dependencies
- Benefits:
- Types can reference any sibling type (they're all in the same Types module)
- Codecs can reference any sibling codec (properly ordered by codec dependencies)
- Union type codecs can now use try-each decoding across multiple sibling schemas
- Generated structure:
module Prefix = struct module Types = struct module Schema_a = struct type t = { ... } end module Union_c = struct type t = A of Schema_a.t | B of Schema_b.t end end module Schema_a = struct include Types.Schema_a let jsont = ... end module Union_c = struct include Types.Union_c let jsont = (* uses Schema_a.jsont *) end end - Preserves the user-facing API:
Prefix.Schema.t,Prefix.Schema.jsont, etc.
Planned Enhancements#
1. Streaming Support#
Priority: High
Add support for text/event-stream media type handling for Server-Sent Events.
Requirements:
- Detect SSE endpoints in OpenAPI spec
- Generate async iterator types for streaming responses
- Requires
requestslibrary enhancement for chunked/streaming reads
Target API:
val stream_events :
t -> unit ->
(Event.t, Openapi.Runtime.api_error) Seq.t
2. File Upload Support#
Priority: High
Handle multipart/form-data with binary parts for file uploads.
Requirements:
- Detect multipart endpoints in spec
- Generate proper file upload functions accepting
Eio.Flow.source - Requires
requestslibrary multipart encoding support
Target API:
val upload_file :
filename:string ->
content_type:string ->
body:_ Eio.Flow.source ->
t -> unit ->
UploadResponse.t
3. Authentication Code Generation#
Priority: Medium
Generate auth header injection based on securitySchemes.
Supported schemes:
apiKey(header, query, cookie)http(basic, bearer)oauth2flows (implicit, password, clientCredentials, authorizationCode)openIdConnect
Target API:
module Auth : sig
type t =
| Api_key of string
| Bearer of string
| Basic of { username: string; password: string }
| OAuth2 of { access_token: string; refresh_token: string option }
val with_auth : t -> client -> client
end
4. Additional Schema Features#
Priority: Medium
4.1 additionalProperties#
Convert to OCaml string maps:
type t = { known_field: string; extra: Jsont.json StringMap.t }
4.2 const#
Generate literal type validation or unit variants.
4.3 default#
Handle default values for optional fields:
- Make fields with defaults optional in constructors
- Use the default value when field is absent during decoding
- Consider generating builder-style constructors for complex schemas
4.4 readOnly/writeOnly#
Generate separate request/response types when fields differ.
5. Requests Library Enhancements#
Priority: Varies
These depend on enhancements to the requests library:
| Feature | Requests Support | OpenAPI Use Case |
|---|---|---|
| Streaming responses | Needed | SSE, large downloads |
| Multipart form data | Needed | File uploads |
| Connection pooling | Nice to have | Performance |
| Retry with backoff | Nice to have | Resilience |
| WebSocket | Future | Real-time APIs |
Architecture Notes#
Current Module Structure#
openapi
├── Spec -- OpenAPI 3.x types
├── Codegen -- Code generation
├── Runtime -- Runtime utilities
└── Nestjs -- NestJS error handling
Generated Code Structure#
generated_client
├── t -- Client type
├── create -- Constructor
└── Module1 -- Per-prefix modules
├── Schema1 -- Per-schema submodules
│ ├── t
│ ├── jsont
│ └── accessors
└── operation1 -- Operations
Design Principles#
- Type safety over flexibility: Prefer typed codecs over
Jsont.json - Minimal runtime: Keep
Runtimemodule small - Idiomatic OCaml: Use modules, not objects
- Eio-native: No blocking IO, cooperative concurrency
- Forward-compatible: Handle unknown fields gracefully
Contributing#
To add a new feature:
- Update
openapi_spec.mlif new OpenAPI fields are needed - Update
openapi_codegen.mlwith analysis and generation - Update
openapi_runtime.mlif new runtime support is needed - Regenerate test specs:
dune exec openapi-gen -- generate ... - Verify with
dune build @checkanddune build @doc