Skip to content
This repository was archived by the owner on Apr 14, 2026. It is now read-only.

Commit 5206145

Browse files
authored
OpenAPI v3.1 support, experimental re-implementation using libopenapi (oapi-codegen#2197)
This is a prototype implementation of a future versions of oapi-codegen. It's almost a full rewrite, heavily inspired by previous code, and lessons learned. - much more flexibility in configuring generated code - move from kin-openapi to libopenapi to support 3.1 and 3.2 specs - webhook support - callback support - incompatible codegen changes to aggregate types (allOf, anyOf, oneOf) - many existing codegen bugs around schemas fixed
1 parent 09919e7 commit 5206145

293 files changed

Lines changed: 41797 additions & 7 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

Makefile

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,34 +19,34 @@ lint: tools
1919
# run the root module explicitly, to prevent recursive calls by re-invoking `make ...` top-level
2020
$(GOBIN)/golangci-lint run ./...
2121
# then, for all child modules, use a module-managed `Makefile`
22-
git ls-files '**/*go.mod' -z | xargs -0 -I{} bash -xc 'cd $$(dirname {}) && env GOBIN=$(GOBIN) make lint'
22+
GOBIN=$(GOBIN) ./scripts/foreach-module.sh lint
2323

2424
lint-ci: tools
2525
# for the root module, explicitly run the step, to prevent recursive calls
2626
$(GOBIN)/golangci-lint run ./... --output.text.path=stdout --timeout=5m
2727
# then, for all child modules, use a module-managed `Makefile`
28-
git ls-files '**/*go.mod' -z | xargs -0 -I{} bash -xc 'cd $$(dirname {}) && env GOBIN=$(GOBIN) make lint-ci'
28+
GOBIN=$(GOBIN) ./scripts/foreach-module.sh lint-ci
2929

3030
generate:
3131
# for the root module, explicitly run the step, to prevent recursive calls
3232
go generate ./...
3333
# then, for all child modules, use a module-managed `Makefile`
34-
git ls-files '**/*go.mod' -z | xargs -0 -I{} bash -xc 'cd $$(dirname {}) && make generate'
34+
GOBIN=$(GOBIN) ./scripts/foreach-module.sh generate
3535

3636
test:
3737
# for the root module, explicitly run the step, to prevent recursive calls
3838
go test -cover ./...
3939
# then, for all child modules, use a module-managed `Makefile`
40-
git ls-files '**/*go.mod' -z | xargs -0 -I{} bash -xc 'cd $$(dirname {}) && make test'
40+
GOBIN=$(GOBIN) ./scripts/foreach-module.sh test
4141

4242
tidy:
4343
# for the root module, explicitly run the step, to prevent recursive calls
4444
go mod tidy
4545
# then, for all child modules, use a module-managed `Makefile`
46-
git ls-files '**/*go.mod' -z | xargs -0 -I{} bash -xc 'cd $$(dirname {}) && make tidy'
46+
GOBIN=$(GOBIN) ./scripts/foreach-module.sh tidy
4747

4848
tidy-ci:
4949
# for the root module, explicitly run the step, to prevent recursive calls
5050
tidied -verbose
5151
# then, for all child modules, use a module-managed `Makefile`
52-
git ls-files '**/*go.mod' -z | xargs -0 -I{} bash -xc 'cd $$(dirname {}) && make tidy-ci'
52+
GOBIN=$(GOBIN) ./scripts/foreach-module.sh tidy-ci

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -393,7 +393,9 @@ We can see that this provides the best means to focus on the implementation of t
393393
- Single-file output
394394
- Support multiple OpenAPI files by having a package-per-OpenAPI file
395395
- Support of OpenAPI 3.0
396-
- OpenAPI 3.1 support is [awaiting upstream support](https://github.com/oapi-codegen/oapi-codegen/issues/373)
396+
- OpenAPI 3.1 support is in experimental form, as a complete rewrite using [libopenapi](https://github.com/pb33f/libopenapi). Please look in the
397+
`./experimental` directory. This is potentially the future V3 of `oapi-codegen` and is functionally complete, just not deeply tested yet. Many OpenAPI 3.1
398+
features are supported, such as webhooks and callbacks.
397399
- Note that this does not include OpenAPI 2.0 (aka Swagger)
398400
- Extract parameters from requests, to reduce work required by your implementation
399401
- Implicit `additionalProperties` are ignored by default ([more details](#additional-properties-additionalproperties))

experimental/Configuration.md

Lines changed: 246 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,246 @@
1+
# Configuration Reference
2+
3+
`oapi-codegen` is configured using a YAML file. All sections are optional — you only need to include what you want to change from the defaults.
4+
5+
Below is a fully annotated configuration file showing every option.
6+
7+
```yaml
8+
# The Go package name for generated code.
9+
# Can also be set with -package flag.
10+
package: myapi
11+
12+
# Output file path.
13+
# Can also be set with -output flag.
14+
# Default: <spec-basename>.gen.go
15+
output: types.gen.go
16+
17+
# Generation controls which parts of the code are generated.
18+
generation:
19+
# Server framework to generate code for.
20+
# Supported: "std-http", "chi", "echo", "echo/v4", "gin", "gorilla", "fiber", "iris"
21+
# Default: "" (no server code generated)
22+
server: std-http
23+
24+
# Generate an HTTP client that returns *http.Response.
25+
# Default: false
26+
client: true
27+
28+
# Generate a SimpleClient wrapper with typed responses.
29+
# Requires client: true.
30+
# Default: false
31+
simple-client: true
32+
33+
# Use model types from an external package instead of generating them locally.
34+
# When set, models are imported rather than generated.
35+
# Default: not set (models are generated locally)
36+
models-package:
37+
path: github.com/org/project/models
38+
alias: models # optional, defaults to last segment of path
39+
40+
# Type mappings: OpenAPI type/format to Go type.
41+
# User values are merged on top of defaults — you only need to specify overrides.
42+
type-mapping:
43+
integer:
44+
default:
45+
type: int # default
46+
formats:
47+
int32:
48+
type: int32 # default
49+
int64:
50+
type: int64 # default
51+
number:
52+
default:
53+
type: float32 # default
54+
formats:
55+
double:
56+
type: float64 # default
57+
boolean:
58+
default:
59+
type: bool # default
60+
string:
61+
default:
62+
type: string # default
63+
formats:
64+
byte:
65+
type: "[]byte" # default
66+
date:
67+
type: Date # default, custom template type
68+
date-time:
69+
type: time.Time # default
70+
import: time
71+
uuid:
72+
type: UUID # default, custom template type
73+
email:
74+
type: Email # default, custom template type
75+
binary:
76+
type: File # default, custom template type
77+
json:
78+
type: json.RawMessage # default
79+
import: encoding/json
80+
# Add your own format mappings:
81+
money:
82+
type: decimal.Decimal
83+
import: github.com/shopspring/decimal
84+
85+
# Name mangling: controls how OpenAPI names become Go identifiers.
86+
# User values are merged on top of defaults.
87+
name-mangling:
88+
# Prefix prepended when a name starts with a digit.
89+
# Default: "N" (e.g., "123foo" becomes "N123foo")
90+
numeric-prefix: "N"
91+
92+
# Prefix prepended when a name conflicts with a Go keyword.
93+
# Default: "" (uses keyword-suffix instead)
94+
keyword-prefix: ""
95+
96+
# Characters that mark word boundaries (next letter is capitalised).
97+
# Default includes most punctuation: - # @ ! $ & = . + : ; _ ~ space ( ) { } [ ] | < > ? / \
98+
word-separators: "-#@!$&=.+:;_~ (){}[]|<>?/\\"
99+
100+
# Words that should remain all-uppercase.
101+
initialisms:
102+
- ACL
103+
- API
104+
- ASCII
105+
- CPU
106+
- CSS
107+
- DB
108+
- DNS
109+
- EOF
110+
- GUID
111+
- HTML
112+
- HTTP
113+
- HTTPS
114+
- ID
115+
- IP
116+
- JSON
117+
- QPS
118+
- RAM
119+
- RPC
120+
- SLA
121+
- SMTP
122+
- SQL
123+
- SSH
124+
- TCP
125+
- TLS
126+
- TTL
127+
- UDP
128+
- UI
129+
- UID
130+
- GID
131+
- URI
132+
- URL
133+
- UTF8
134+
- UUID
135+
- VM
136+
- XML
137+
- XMPP
138+
- XSRF
139+
- XSS
140+
- SIP
141+
- RTP
142+
- AMQP
143+
- TS
144+
145+
# Characters that get replaced with words when they appear at the start of a name.
146+
character-substitutions:
147+
"$": DollarSign
148+
"-": Minus
149+
"+": Plus
150+
"&": And
151+
"|": Or
152+
"~": Tilde
153+
"=": Equal
154+
">": GreaterThan
155+
"<": LessThan
156+
"#": Hash
157+
".": Dot
158+
"*": Asterisk
159+
"^": Caret
160+
"%": Percent
161+
"_": Underscore
162+
"@": At
163+
"!": Bang
164+
"?": Question
165+
"/": Slash
166+
"\\": Backslash
167+
":": Colon
168+
";": Semicolon
169+
"'": Apos
170+
"\"": Quote
171+
"`": Backtick
172+
"(": LParen
173+
")": RParen
174+
"[": LBracket
175+
"]": RBracket
176+
"{": LBrace
177+
"}": RBrace
178+
179+
# Name substitutions: direct overrides for specific generated names.
180+
name-substitutions:
181+
# Override type names during generation.
182+
type-names:
183+
foo: MyCustomFoo # Schema "foo" generates type "MyCustomFoo" instead of "Foo"
184+
# Override property/field names during generation.
185+
property-names:
186+
bar: MyCustomBar # Property "bar" generates field "MyCustomBar" instead of "Bar"
187+
188+
# Import mapping: resolve external $ref targets to Go packages.
189+
# Required when your spec references schemas from other files.
190+
import-mapping:
191+
../common/api.yaml: github.com/org/project/common
192+
https://example.com/specs/shared.yaml: github.com/org/shared
193+
# Use "-" to indicate types should stay in the current package
194+
./local-types.yaml: "-"
195+
196+
# Content types: regexp patterns controlling which media types generate models.
197+
# Only request/response bodies with matching content types will have types generated.
198+
# Default: JSON types only.
199+
content-types:
200+
- "^application/json$"
201+
- "^application/.*\\+json$"
202+
# Add custom patterns as needed:
203+
# - "^application/xml$"
204+
# - "^text/plain$"
205+
206+
# Content type short names: maps content type patterns to short names
207+
# used in generated type names (e.g., "FindPetsJSONResponse").
208+
content-type-short-names:
209+
- pattern: "^application/json$"
210+
short-name: JSON
211+
- pattern: "^application/xml$"
212+
short-name: XML
213+
- pattern: "^text/plain$"
214+
short-name: Text
215+
# ... defaults cover JSON, XML, Text, HTML, Binary, Multipart, Form
216+
217+
# Struct tags: controls which struct tags are generated and their format.
218+
# Uses Go text/template syntax. If specified, completely replaces the defaults.
219+
# Default: json and form tags.
220+
struct-tags:
221+
tags:
222+
- name: json
223+
template: '{{if .JSONIgnore}}-{{else}}{{ .FieldName }}{{if .OmitEmpty}},omitempty{{end}}{{if .OmitZero}},omitzero{{end}}{{end}}'
224+
- name: form
225+
template: '{{if .JSONIgnore}}-{{else}}{{ .FieldName }}{{if .OmitEmpty}},omitempty{{end}}{{end}}'
226+
# Add additional tags:
227+
- name: yaml
228+
template: '{{ .FieldName }}{{if .OmitEmpty}},omitempty{{end}}'
229+
- name: db
230+
template: '{{ .FieldName }}'
231+
```
232+
233+
## Struct tag template variables
234+
235+
The struct tag templates have access to the following fields:
236+
237+
| Variable | Type | Description |
238+
|----------|------|-------------|
239+
| `.FieldName` | `string` | The original field name from the OpenAPI spec |
240+
| `.GoFieldName` | `string` | The Go identifier name after name mangling |
241+
| `.IsOptional` | `bool` | Whether the field is optional in the schema |
242+
| `.IsNullable` | `bool` | Whether the field is nullable |
243+
| `.IsPointer` | `bool` | Whether the field is rendered as a pointer type |
244+
| `.OmitEmpty` | `bool` | Whether `omitempty` should be applied (from extensions or optionality) |
245+
| `.OmitZero` | `bool` | Whether `omitzero` should be applied (from `x-oapi-codegen-omitzero` extension) |
246+
| `.JSONIgnore` | `bool` | Whether the field should be ignored in JSON (from `x-go-json-ignore` extension) |

experimental/Makefile

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
SHELL:=/bin/bash
2+
3+
YELLOW := \e[0;33m
4+
RESET := \e[0;0m
5+
6+
GOVER := $(shell go env GOVERSION)
7+
GOMINOR := $(shell bash -c "cut -f1 -d' ' <<< \"$(GOVER)\" | cut -f2 -d.")
8+
9+
define execute-if-go-124
10+
@{ \
11+
if [[ 24 -le $(GOMINOR) ]]; then \
12+
$1; \
13+
else \
14+
echo -e "$(YELLOW)Skipping task as you're running Go v1.$(GOMINOR).x which is < Go 1.24, which this module requires$(RESET)"; \
15+
fi \
16+
}
17+
endef
18+
19+
lint:
20+
$(call execute-if-go-124,$(GOBIN)/golangci-lint run ./...)
21+
22+
lint-ci:
23+
$(call execute-if-go-124,$(GOBIN)/golangci-lint run ./... --output.text.path=stdout --timeout=5m)
24+
25+
generate:
26+
$(call execute-if-go-124,go generate ./...)
27+
28+
test:
29+
@echo "Skipping tests in experimental module"
30+
31+
tidy:
32+
$(call execute-if-go-124,go mod tidy)
33+
34+
tidy-ci:
35+
$(call execute-if-go-124,tidied -verbose)

0 commit comments

Comments
 (0)