Skip to content

Releases

v1.9.0: Slimmer dependencies and a pluggable JSON codec

Choose a tag to compare

@github-actions github-actions released this 10 Nov 22:18
1b74ca8

This release removes the strictmiddleware packages, which pulled every supported web framework into the dependency graph of every program that imports the runtime, and which generated code has not used since oapi-codegen v2.7.0. It adds a process-wide JSON codec that the upcoming oapi-codegen v2.9.0 will route all generated JSON encoding and decoding through, binds uuid.UUID fields in deepObject parameters, and fixes label and matrix path parameters whose value contains the style's delimiter. The minimum Go version is now 1.26.

Notable Changes

☢️ Breaking: the strictmiddleware packages are gone

The strictmiddleware/{echo,echo-v5,gin,iris,nethttp} packages held a handful of StrictHandlerFunc and StrictMiddlewareFunc type definitions, and nothing else. To define them, the runtime had to import Echo, Gin and Iris, so every program importing github.com/oapi-codegen/runtime dragged all three frameworks and their transitive dependencies into its go.mod, whether it used them or not. Removing the packages (#181, closes #180) takes the root module from 5 direct and 66 indirect requirements down to 4 direct and 1 indirect, with no web framework among them. The separate strictmiddleware/echo-v5 module is removed as well.

Code generated by oapi-codegen v2.7.0 and later declares these types itself and never imports the packages, so for anyone on a current generator the upgrade is a go get and a go mod tidy that deletes a lot of lines. Code generated by oapi-codegen v2.6.0 or earlier still imports them and will not compile against this release.

Important

If your project uses generated code from oapi-codegen v2.6.0 or earlier, stay on runtime v1.8.0 until you have regenerated with v2.7.0 or later. Do not upgrade the runtime first.

Pluggable JSON codec

Everything the runtime encodes or decodes as JSON now goes through a single, process-wide codec (#183). The default is encoding/json, unchanged, and a program can replace it in its main package:

runtime.SetJSONCodec(myCodec) // any value with Marshal(any) ([]byte, error) and Unmarshal([]byte, any) error

The API is:

  • JSONCodec: the two-method interface a replacement implements. StdJSONCodec is encoding/json's pair, and can be embedded to override just one method, for instance to turn off HTML escaping.
  • StreamingJSONCodec: an optional second interface, MarshalWrite(io.Writer, any) and UnmarshalRead(io.Reader, any), for codecs that can stream request and response bodies instead of buffering them. A codec without it is buffered through Marshal and Unmarshal.
  • JSONMarshal, JSONUnmarshal, JSONMarshalWrite and JSONUnmarshalRead: the functions that generated code and the runtime itself call. JSONUnmarshalRead returns io.EOF for an empty body whichever codec is in use, which is how strict servers detect an optional body that was left out.
  • SetJSONCodec(nil) restores the default.

Within the runtime, deepObject and form parameter encoding, BindJSONBody, and the types package's Date, Duration, Email and File all use the codec already. Generated code still calls encoding/json directly today. oapi-codegen v2.9.0 will switch every generated json.Marshal, json.Unmarshal and strict response encoder over to these functions, at which point one SetJSONCodec call covers a whole program: models, clients and servers. This replaces the per-package json-encoding configuration proposed in oapi-codegen/oapi-codegen#2371, where the design discussion lives, and it is the intended path to encoding/json/v2. Thanks to @CRaLFa for the implementation.

uuid.UUID fields bind in deepObject parameters

A deepObject query parameter with a format: uuid field, such as ?filter[entity]=6ba7b810-9dad-11d1-80b4-00c04fd430c8, failed with unhandled type: uuid.UUID. uuid.UUID is a [16]byte, so it never reached the branch that checks for encoding.TextUnmarshaler. Array-kind types that implement UnmarshalText now bind, as they already did everywhere else in the runtime; uuid.UUID, *uuid.UUID, slices and map values all work. Other fixed-size arrays are still rejected as before. Thanks to @youdie006 (#171).

label and matrix scalar path parameters are no longer split

A scalar label or matrix path parameter whose value contained the style's delimiter, such as big,red sent as .big%2Cred, was rejected with "expected single value, got N parts" once the router had decoded the segment. The primitive branch now strips only the style prefix and never splits the value, matching the fix #114 made for the simple style. Arrays, structs and maps are unchanged (#182, closes #175).

Go 1.26

The module now requires Go 1.26, the oldest supported Go release, and all remaining dependencies are at their latest versions (#178).

☢️ Breaking changes

🚀 New features and improvements

🐛 Bug fixes

📦 Dependency updates

8 changes

🔐 Security

Sponsors

We would like to thank our sponsors for their support during this release.

DevZero logo

Cybozu logo

New contributors

v1.8.0: Bind the request body value the validator saw

Choose a tag to compare

@github-actions github-actions released this 23 Oct 06:28
c0b2ca2

This release adds a request body binder that reuses the value a kin-openapi validation middleware already decoded, so strict servers no longer decode the body a second time with different matching rules. It also rounds out types.Date's text encoding and fixes a deepObject serialization quirk. Existing binding and serialization behaviour is unchanged unless generated code calls the new function. The root module's dependencies are unchanged; the separate strictmiddleware/echo-v5 module now requires Echo v5.4.2.

Notable Changes

BindJSONBody: bind exactly what the validator validated

Strict servers generated by oapi-codegen decode the JSON request body with encoding/json after a validation middleware has already decoded and validated it. The two decoders do not agree on member names: encoding/json matches struct fields case-insensitively, so a body like {"role":"user","ROLE":"admin"} validates as role: user and then binds as Role: "admin". Anything the validator checked can be bypassed by a differently-cased duplicate.

BindJSONBody(r *http.Request, v any) error closes that gap. When the request has passed through a validator built on kin-openapi v0.151.0 or later with openapi3filter.Options.KeepValidatedValue set, the body carries the value the validator decoded, and BindJSONBody binds that value into the destination with BindJSONValue(value, v any) error instead of reading the body again. Binding follows encoding/json's rules (struct tags, embedded structs, json.Unmarshaler and encoding.TextUnmarshaler, base64 for []byte, float64 for any, json.Number preserved) with one deliberate difference: member names are matched to struct fields exactly.

Without a validated value, or when the value cannot be bound into the destination type, BindJSONBody falls back to json.NewDecoder(r.Body).Decode(v), so error responses are unchanged for everyone who is not running a validator. The oapi-codegen validation middlewares enable KeepValidatedValue by default as of nethttp-middleware v1.3.1, echo-middleware v1.2.1, echo-v5-middleware v0.2.1, gin-middleware v1.2.1 and iris-middleware v1.2.1. The net/http strict server template that calls BindJSONBody is landing in oapi-codegen separately (oapi-codegen/oapi-codegen#2605); the runtime side ships first so generated code can rely on it.

types.Date implements encoding.TextMarshaler

Date had an UnmarshalText that parses 2006-01-02 but no MarshalText, so it inherited time.Time's RFC 3339 one. Any codec that pairs TextMarshaler with TextUnmarshaler (encoding/xml, DynamoDB mappers, and others) therefore wrote a Date it could not read back. MarshalText now formats with DateFormat, matching MarshalJSON, String and UnmarshalText. Thanks to @jusabe-kreditz.

deepObject no longer emits param[]=null for nil maps and slices

Marshalling a deepObject parameter whose field held a typed nil map or nil slice produced a param[]=null pair, because the intermediate json.Marshal turned the nil into null. Such fields are now omitted, as an empty map or slice already was. Nil pointers are still serialized as null, as before. Thanks to @SKalt (#160).

🚀 New features and improvements

🐛 Bug fixes

📦 Dependency updates

6 changes

Sponsors

We would like to thank our sponsors for their support during this release.

DevZero logo

Cybozu logo

New contributors

v1.7.0: Extensions for OpenAPI 3.1 parameter binding

Choose a tag to compare

@github-actions github-actions released this 16 Aug 05:35
25e2d35

This release teaches the parameter binders about OpenAPI 3.1 multi-type unions, and fixes a long-standing panic on the request binding path. As with v1.6.0, new behavior is controlled by explicit settings rather than assumptions: binding stays exactly as it was unless the new options are used.

Notable Changes

Binding OpenAPI 3.1 multi-type union parameters

OpenAPI 3.1 allows a parameter's type to be a list, such as type: [string, integer]. Go has no type meaning "one of these", so generated code maps such parameters to any — which the binders previously rejected outright with can not bind to destination of type: interface, making these parameters unusable.

The binder options structs (BindStyledParameterOptions, BindQueryParameterOptions, BindStringToObjectOptions) gain a Types []string field carrying the union's member list. It is only consulted when the destination is an any; binding into every concrete Go type is completely unchanged. The value binds to the first member that parses, trying boolean, integer, number, then string — most restrictive first, since a string always parses. Member detection follows the JSON number grammar (RFC 8259), so values like 007 or +1 bind as strings rather than being silently reinterpreted as numbers.

The bound value's dynamic type is always one of bool, int64, float64, string, or (with format: byte) []byte, so a handler's type switch is stable regardless of what the spec's format says. Applications that want format: int32 / format: float to narrow the produced types to int32 / float32 can opt in via a new package-level setting, following the same pattern as DefaultQueryEncoder from v1.6.0:

func init() {
    runtime.NarrowUnionNumericFormats = true
}

Generator support for emitting Types is landing in oapi-codegen separately; the runtime side ships first so generated code can rely on it. Arrays of unions and deepObject-style parameters are not covered yet — see the Types field documentation for the exact scope.

Fix for a panic when binding numeric values into slice destinations

Since v1.2.0, binding a string that happens to parse as an integer into a non-[]byte slice destination panicked with reflect: call of reflect.Value.OverflowInt on slice Value, instead of returning an error. This was reachable from generated code on the request path: a nullable.Nullable[[]string] query parameter using the default form/explode serialization would panic on ?p=123 while returning a normal binding error on ?p=abc. These cases now return a clean can not bind to destination of type: slice error.

🚀 New features and improvements

🐛 Bug fixes

✍ Other changes

📦 Dependency updates

6 changes

Sponsors

We would like to thank our sponsors for their support during this release.

DevZero logo

Cybozu logo

  • No new contributors

Allow customization of parameter encoding

Choose a tag to compare

@github-actions github-actions released this 16 Jul 20:21
01be2fa

This is a small release which adds a global encoding setting on the runtime, to allow users to customize how to handle spaces in query arguments. This is a new minor release, since we're adding new API, even though this is a very minor feature. I imagine that in the future, we will add more settings, rather than making behavior assumptions.

🚀 New features and improvements

📦 Dependency updates

  • chore(deps): update module github.com/golangci/golangci-lint to v2.12.2 (#112) @renovate[bot]

Sponsors

We would like to thank our sponsors for their support during this release.

Sponsors

We would like to thank our sponsors for their support during this release.

DevZero logo

Cybozu logo

v1.5.0: RFC3339 durations, and bug fixes

Choose a tag to compare

@github-actions github-actions released this 12 Jul 23:03
540d34a

This is mainly a bugfix release, but we're bumping the minor version since we also introduce a new type, Duration into our types/ package, which allows for parsing and emitting RFC3339 durations. Rather than trying to parse a duration string into a time.Duration, which requires assumptions that may not be right for everyone, we decided not to make those decisions and just store all possible fields as provided. Users can convert this to Go Duration as they see fit.

🚀 New features and improvements

🐛 Bug fixes

  • Let generated code declare whether styled parameter values are escaped (#143) @mromaszewicz
  • Prefer the form struct tag over json for form encoding (#140) @mromaszewicz
  • Support binding deepObject parameters into map[string]interface{} (#139) @mromaszewicz

📝 Documentation updates

  • Explain how to send nested objects when style serialization fails (#141) @mromaszewicz

📦 Dependency updates

6 changes
  • fix(deps): update module github.com/labstack/echo/v5 to v5.3.0 (#142) @renovate[bot]
  • chore(deps): update golang/govulncheck-action action to v1.1.0 (#137) @renovate[bot]
  • chore(deps): update oapi-codegen/actions action to v0.8.0 (#130) @renovate[bot]
  • fix(deps): update module github.com/labstack/echo/v5 to v5.2.1 - autoclosed (#126) @renovate[bot]
  • chore(deps): update release-drafter/release-drafter action to v7.5.1 - autoclosed (#124) @renovate[bot]
  • chore(deps): update github/codeql-action action to v4.37.0 (#123) @renovate[bot]

Sponsors

We would like to thank our sponsors for their support during this release.

DevZero logo

Cybozu logo

Bug fix for required parameters

Choose a tag to compare

@github-actions github-actions released this 20 Jun 06:11
7afeea8

This is a bug fix to address a regression introduced in oapi-codegen v2.7.0

🐛 Bug fixes

Sponsors

We would like to thank our sponsors for their support during this release.

DevZero logo

Cybozu logo

Bug fixes

Choose a tag to compare

@github-actions github-actions released this 19 May 15:41
2755f15

This is a bug fix release.

Changes in v1.4.0, coupled with changes in v2.7.0 of oapi-codegen exposed some new problems. deepObject style marshaling behavior now supports encoding unicode. UTF-8 can't be directly included in parameters, so we need to % escape it.

Form binding now detects maps, which makes binding to a Nullable possible. We can't use generics around Nullable[T], so we handle maps generically, assuming they're a Nullable with its behavior assumptions.

🐛 Bug fixes

📦 Dependency updates

Sponsors

We would like to thank our sponsors for their support during this release.

DevZero logo

Cybozu logo

Parameter handling improvements and fixes

Choose a tag to compare

@github-actions github-actions released this 08 Apr 21:33
247b459

This release fixes some missing edge cases in parameter binding and styling. We now handle all the permutations of style and explode, for the first time. Lots of tests have been added to catch regressions.

🚀 New features and improvements

  • Improve deepobject unmarshalling to support nullable.Nullable and encode.TextUnmarshaler (#45) @j-waters
  • feat: support spaceDelimited and pipeDelimited query parameter binding (#117) @mromaszewicz

🐛 Bug fixes

  • Fix form/explode=false incorrectly splitting primitive string values on commas (#119) @f-kanari

📦 Dependency updates

  • fix(deps): update module github.com/labstack/echo/v4 to v4.15.1 (#105) @renovate[bot]
  • fix(deps): update module github.com/labstack/echo/v5 to v5.1.0 (#120) @renovate[bot]
  • chore(deps): update release-drafter/release-drafter action to v7 (#113) @renovate[bot]

Sponsors

We would like to thank our sponsors for their support during this release.

DevZero logo

Cybozu logo

Fix a parameter binding regression

Choose a tag to compare

@github-actions github-actions released this 25 Mar 17:56
00e51fe

v1.3.0 introduced a regression around binding styled parameters into primitive type destinations. This regression was due to a
fix in binding matrix and label parameters. Sorry about that.

🐛 Bug fixes

Sponsors

We would like to thank our sponsors for their support during this release.

DevZero logo

Cybozu logo

Echo V5, more parameter handling options, bug fixes

Choose a tag to compare

@github-actions github-actions released this 18 Mar 16:47
ee2e591

🚀 New features and improvements

  • feat: improve parameter handling (#109) @mromaszewicz
    Parameters now support the allowReserved property from OpenAPI 3.0.
  • feat: add support for echo v5 (#89) @jinuthankachan

🐛 Bug fixes

  • Fix: Query param deepObject return without assign on !required (#68) @voro015
  • fix: strip style prefix for label/matrix primitive parameters (#99) (#100) @mromaszewicz
  • fix: parse un-exploded query param to map (#101) @alexdulin
  • fix: respect Binder interface for primitive types in BindStringToObject (#86) @vikstrous

👻 Maintenance

📦 Dependency updates

Sponsors

We would like to thank our sponsors for their support during this release.

DevZero logo

Cybozu logo