Repository: deepmap/oapi-codegen
Pull request #948 (2023-01-30 17:25:43)

feat: support nullable additional properties
Adds support for `nullable: true` schemas inside additional properties.


Patch:
diff --git a/README.md b/README.md
index f51aa46..572c8b3 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,4 @@
-OpenAPI Client and Server Code Generator
-----------------------------------------
+## OpenAPI Client and Server Code Generator
 
 ⚠️ This README may be for the latest development version, which may contain
 unreleased changes. Please ensure you're looking at the README for the latest
@@ -16,7 +15,8 @@ you can focus on implementing the business logic for your service.
 
 We have chosen to focus on [Echo](https://github.com/labstack/echo) as
 our default HTTP routing engine, due to its speed and simplicity for the generated
-stubs, and [Chi](https://github.com/go-chi/chi), and [Gin](https://github.com/gin-gonic/gin)
+stubs. [Chi](https://github.com/go-chi/chi), [Gin](https://github.com/gin-gonic/gin),
+[gorilla/mux](https://github.com/gorilla/mux), and [Fiber](https://github.com/gofiber/fiber)
 have also been added by contributors as additional routers. We chose Echo because
 the `Context` object is a mockable interface, and it allows for some advanced
 testing.
@@ -45,12 +45,12 @@ directory does a lot of that for you. You would run it like so:
 Let's go through that `petstore.gen.go` file to show you everything which was
 generated.
 
-
 ## Generated Server Boilerplate
 
 The `/components/schemas` section in OpenAPI defines reusable objects, so Go
 types are generated for these. The Pet Store example defines `Error`, `Pet`,
 `Pets` and `NewPet`, so we do the same in Go:
+
 ```go
 // Error defines model for Error.
 type Error struct {
@@ -121,11 +121,12 @@ will be passed as arguments to your function, since they are mandatory.
 Remaining arguments can be passed in headers, query arguments or cookies. Those
 will be written to a `params` object. Look at the `FindPets` function above, it
 takes as input `FindPetsParams`, which is defined as follows:
- ```go
+
+```go
 // Parameters object for FindPets
 type FindPetsParams struct {
-    Tags  *[]string `json:"tags,omitempty"`
-    Limit *int32   `json:"limit,omitempty"`
+   Tags  *[]string `json:"tags,omitempty"`
+   Limit *int32   `json:"limit,omitempty"`
 }
 ```
 
@@ -135,6 +136,7 @@ specified, the pointer will be non-`nil`, and you can read its value.
 
 If you changed the OpenAPI specification to make the parameter required, the
 `FindPetsParams` structure will contain the type by value:
+
 ```go
 type FindPetsParams struct {
     Tags  *[]string `json:"tags,omitempty"`
@@ -143,6 +145,7 @@ type FindPetsParams struct {
 ```
 
 ### Registering handlers
+
 There are a few ways of registering your http handler based on the type of server generated i.e. `-generate server` or `-generate chi-server`
 
 <details><summary><code>Echo</code></summary>
@@ -152,6 +155,7 @@ Code generated using `-generate server`.
 The usage of `Echo` is out of scope of this doc, but once you have an
 echo instance, we generate a utility function to help you associate your handlers
 with this autogenerated code. For the pet store, it looks like this:
+
 ```go
 func RegisterHandlers(router codegen.EchoRouter, si ServerInterface) {
     wrapper := ServerInterfaceWrapper{
@@ -168,6 +172,7 @@ The wrapper functions referenced above contain generated code which pulls
 parameters off the `Echo` request context, and unmarshals them into Go objects.
 
 You would register the generated handlers as follows:
+
 ```go
 func SetupHandler() {
     var myApi PetStoreImpl  // This implements the pet store interface
@@ -196,6 +201,7 @@ func SetupHandler() {
     r.Mount("/", Handler(&myApi))
 }
 ```
+
 </summary></details>
 
 <details><summary><code>Gin</code></summary>
@@ -205,6 +211,7 @@ Code generated using `-generate gin`.
 The usage of `gin` is out of scope of this doc, but once you have an
 gin instance, we generate a utility function to help you associate your handlers
 with this autogenerated code. For the pet store, it looks like this:
+
 ```go
 // RegisterHandlersWithOptions creates http.Handler with additional options
 func RegisterHandlersWithOptions(router *gin.Engine, si ServerInterface, options GinServerOptions) *gin.Engine {
@@ -241,6 +248,7 @@ func SetupHandler() {
     r = api.RegisterHandlers(r, petStore)
 }
 ```
+
 </summary></details>
 
 <details><summary><code>net/http</code></summary>
@@ -266,8 +274,8 @@ Alternatively, [Gorilla](https://github.com/gorilla/mux) is also 100% compatible
 
 #### Strict server generation
 
-oapi-codegen also supports generating RPC inspired strict server, that will parse request bodies and encode responses. 
-The main points of this code is to automate some parsing, abstract user code from server specific code, 
+oapi-codegen also supports generating RPC inspired strict server, that will parse request bodies and encode responses.
+The main points of this code is to automate some parsing, abstract user code from server specific code,
 and also to force user code to comply with the schema.
 It supports binding of `application/json` and `application/x-www-form-urlencoded` to a struct, for `multipart` requests
 it generates a `multipart.Reader`, which can be used to either manually iterating over parts or using `runtime.BindMultipart`
@@ -279,6 +287,7 @@ Content-Type header, status code and will marshal the response data. You can als
 cause an `Internal Server Error` response.
 
 Short example:
+
 ```go
 type PetStoreImpl struct {}
 func (*PetStoreImpl) GetPets(ctx context.Context, request GetPetsRequestObject) (GetPetsResponseObject, error) {
@@ -287,10 +296,12 @@ func (*PetStoreImpl) GetPets(ctx context.Context, request GetPetsRequestObject)
     return GetPets200JSONResponse(result), nil
 }
 ```
-For a complete example see `/examples/petstore-expanded/strict`.
+
+For a complete example see [`examples/petstore-expanded/strict`](https://github.com/deepmap/oapi-codegen/tree/master/examples/petstore-expanded/strict).
 
 Code is generated with a configuration flag `generate: strict-server: true` along with any other server (echo, chi, gin and gorilla are supported).
 The generated strict wrapper can then be used as an implementation for `ServerInterface`. Setup example:
+
 ```go
 func SetupHandler() {
     var myApi PetStoreImpl
@@ -317,20 +328,22 @@ defaults to `false` and we don't generate this boilerplate. If you would like
 an object to accept `additionalProperties`, specify a schema for `additionalProperties`.
 
 Say we declared `NewPet` above like so:
+
 ```yaml
-    NewPet:
-      required:
-        - name
-      properties:
-        name:
-          type: string
-        tag:
-          type: string
-      additionalProperties:
-        type: string
+NewPet:
+  required:
+    - name
+  properties:
+    name:
+      type: string
+    tag:
+      type: string
+  additionalProperties:
+    type: string
 ```
 
 The Go code for `NewPet` would now look like this:
+
 ```go
 // NewPet defines model for NewPet.
 type NewPet struct {
@@ -342,6 +355,7 @@ type NewPet struct {
 
 The additionalProperties, of type `string` become `map[string]string`, which maps
 field names to instances of the `additionalProperties` schema.
+
 ```go
 // Getter for additional properties for NewPet. Returns the specified
 // element and whether it was found
@@ -359,23 +373,25 @@ func (a NewPet) MarshalJSON() ([]byte, error) {...}w
 
 There are many special cases for `additionalProperties`, such as having to
 define types for inner fields which themselves support additionalProperties, and
-all of them are tested via the `internal/test/components` schemas and tests. Please
+all of them are tested via the [`internal/test/components`](https://github.com/deepmap/oapi-codegen/tree/master/internal/test/components) schemas and tests. Please
 look through those tests for more usage examples.
 
 #### oneOf/anyOf/allOf support
 
 - `oneOf` and `anyOf` are implemented using delayed parsing with the help of `json.RawMessage`.
-The following schema will result in a type that has methods such as `AsCat`, `AsDog`, `FromCat`, `FromDog`, `MergeCat`, `MergeDog`. If the schema also includes a discriminator the generated code will also have methods such as `Discriminator`, `ValueByDiscriminator` and will force discriminator value in `From` methods.
+  The following schema will result in a type that has methods such as `AsCat`, `AsDog`, `FromCat`, `FromDog`, `MergeCat`, `MergeDog`. If the schema also includes a discriminator the generated code will also have methods such as `Discriminator`, `ValueByDiscriminator` and will force discriminator value in `From` methods.
+
 ```yaml
 schema:
   oneOf:
     - $ref: '#/components/schemas/Cat'
     - $ref: '#/components/schemas/Dog'
 ```
+
 - `allOf` is supported, by taking the union of all the fields in all the
-    component schemas. This is the most useful of these operations, and is
-    commonly used to merge objects with an identifier, as in the
-    `petstore-expanded` example.
+  component schemas. This is the most useful of these operations, and is
+  commonly used to merge objects with an identifier, as in the
+  `petstore-expanded` example.
 
 ## Generated Client Boilerplate
 
@@ -427,21 +443,21 @@ takes the same arguments. It's difficult to handle any arbitrary body that
 Swagger supports, so we've done some special casing for bodies, and you may get
 more than one function for an operation with a request body.
 
-1) If you have more than one request body type, meaning more than one media
- type, you will have a generic handler of this form:
+1.  If you have more than one request body type, meaning more than one media
+    type, you will have a generic handler of this form:
 
-        AddPet(ctx context.Context, contentType string, body io.Reader)
+           AddPet(ctx context.Context, contentType string, body io.Reader)
 
-2) If you have only a JSON request body, you will get:
+2.  If you have only a JSON request body, you will get:
 
-        AddPet(ctx context.Context, body NewPet)
+         AddPet(ctx context.Context, body NewPet)
 
-3) If you have multiple request body types, which include a JSON type you will
- get two functions. We've chosen to give the JSON version a shorter name, as
- we work with JSON and don't want to wear out our keyboards.
+3.  If you have multiple request body types, which include a JSON type you will
+    get two functions. We've chosen to give the JSON version a shorter name, as
+    we work with JSON and don't want to wear out our keyboards.
 
-        AddPet(ctx context.Context, body NewPet)
-        AddPetWithBody(ctx context.Context, contentType string, body io.Reader)
+           AddPet(ctx context.Context, body NewPet)
+           AddPetWithBody(ctx context.Context, contentType string, body io.Reader)
 
 The Client object above is fairly flexible, since you can pass in your own
 `http.Client` and a request editing callback. You can use that callback to add
@@ -473,30 +489,30 @@ will correspond to your request schema. They map one-to-one to the functions on
 the client, except that we always generate the generic non-JSON body handler.
 
 There are some caveats to using this code.
+
 - exploded, form style query arguments, which are the default argument format
- in OpenAPI 3.0 are undecidable. Say that I have two objects, one composed of
- the fields `(name=bob, id=5)` and another which has `(name=shoe, color=brown)`.
- The first parameter is named `person` and the second is named `item`. The
- default marshaling style for query args would result in
- `/path/?name=bob,id=5&name=shoe,color=brown`. In order to tell what belongs
- to which object, we'd have to look at all the parameters and try to deduce it,
- but we're lazy, so we didn't. Don't use exploded form style arguments if
- you're passing around objects which have similar field names. If you
- used unexploded form parameters, you'd have
- `/path/?person=name,bob,id,5&item=name,shoe,color,brown`, which an be
- parsed unambiguously.
+  in OpenAPI 3.0 are undecidable. Say that I have two objects, one composed of
+  the fields `(name=bob, id=5)` and another which has `(name=shoe, color=brown)`.
+  The first parameter is named `person` and the second is named `item`. The
+  default marshaling style for query args would result in
+  `/path/?name=bob,id=5&name=shoe,color=brown`. In order to tell what belongs
+  to which object, we'd have to look at all the parameters and try to deduce it,
+  but we're lazy, so we didn't. Don't use exploded form style arguments if
+  you're passing around objects which have similar field names. If you
+  used unexploded form parameters, you'd have
+  `/path/?person=name,bob,id,5&item=name,shoe,color,brown`, which an be
+  parsed unambiguously.
 
 - Parameters can be defined via `schema` or via `content`. Use the `content` form
- for anything other than trivial objects, they can marshal to arbitrary JSON
- structures. When you send them as cookie (`in: cookie`) arguments, we will
- URL encode them, since JSON delimiters aren't allowed in cookies.
+  for anything other than trivial objects, they can marshal to arbitrary JSON
+  structures. When you send them as cookie (`in: cookie`) arguments, we will
+  URL encode them, since JSON delimiters aren't allowed in cookies.
 
 ## Using SecurityProviders
 
 If you generate client-code, you can use some default-provided security providers
 which help you to use the various OpenAPI 3 Authentication mechanism.
 
-
 ```go
     import (
         "github.com/deepmap/oapi-codegen/pkg/securityprovider"
@@ -556,54 +572,63 @@ which help you to use the various OpenAPI 3 Authentication mechanism.
   will override any default value. This extended property isn't supported in all parts of
   OpenAPI, so please refer to the spec as to where it's allowed. Swagger validation tools will
   flag incorrect usage of this property.
+- `x-go-type-name`: This property allows for assigning a Go type name to some part of a schema,
+  such as generating a type name for an anonymous object inside another object, or renaming
+  an enum. It differs from `x-go-type`, in that it doesn't completely replace some type reference,
+  but simply names it.
 - `x-go-json-ignore`: sets tag to `-` to ignore the field in json completely.
 - `x-oapi-codegen-extra-tags`: adds extra Go field tags to the generated struct field. This is
   useful for interfacing with tag based ORM or validation libraries. The extra tags that
-  are added are in addition to the regular json tags that are generated. If you specify your 
-  own `json` tag, you will override the default one. 
-
-    ```yaml
-    components:
-      schemas:
-        Object:
-          properties:
-            name:
-              type: string
-              x-oapi-codegen-extra-tags:
-                tag1: value1
-                tag2: value2
-    ```
-  In the example above, field `name` will be declared as: 
-  
+  are added are in addition to the regular json tags that are generated. If you specify your
+  own `json` tag, you will override the default one.
+
+  ```yaml
+  components:
+    schemas:
+      Object:
+        properties:
+          name:
+            type: string
+            x-oapi-codegen-extra-tags:
+              tag1: value1
+              tag2: value2
+  ```
+
+  In the example above, field `name` will be declared as:
+
   ```
   Name string `json:"name" tag1:"value1" tag2:"value2"`
   ```
+
 - `x-go-type-import`: adds extra Go imports to your generated code. It can help you, when you want to
-   choose your own import package for `x-go-type`.
+  choose your own import package for `x-go-type`.
 
   ```yaml
-    schemas:
-      Pet:
-        properties:
-          age:
-            x-go-type: myuuid.UUID
-            x-go-type-import:
-              name: myuuid
-              path: github.com/google/uuid
+  schemas:
+    Pet:
+      properties:
+        age:
+          x-go-type: myuuid.UUID
+          x-go-type-import:
+            name: myuuid
+            path: github.com/google/uuid
   ```
+
   After code generation you will get this:
+
   ```go
     import (
         ...
         myuuid "github.com/google/uuid"
     )
-    
+
   //Pet defines model for Pet.
     type Pet struct {
         Age *myuuid.UUID `json:"age,omitempty"`
     }
 
   ```
+
   `name` is an optional parameter. Example:
 
   ```yaml
@@ -623,44 +648,44 @@ which help you to use the various OpenAPI 3 Authentication mechanism.
 
   ```go
   import (
-	  "github.com/google/uuid"
+    "github.com/google/uuid"
   )
 
   // Pet defines model for Pet.
   type Pet struct {
-	  Age uuid.UUID `json:"age"`
+    Age uuid.UUID `json:"age"`
   }
   ```
 
 - `x-enum-varnames`: supplies other enum names for the corresponding values. (alias: `x-enumNames`)
 
-    ```yaml
-    components:
-      schemas:
-        Object:
-          properties:
-            category:
-              type: integer
-              enum: [0, 1, 2]
-              x-enum-varnames:
-                - notice
-                - warning
-                - urgent
-    ```
-
-    After code generation you will get this result:
-
-    ```go
-    // Defines values for ObjectCategory.
-    const (
-    	Notice  ObjectCategory = 0
-    	Urgent  ObjectCategory = 2
-    	Warning ObjectCategory = 1
-    )
+  ```yaml
+  components:
+    schemas:
+      Object:
+        properties:
+          category:
+            type: integer
+            enum: [0, 1, 2]
+            x-enum-varnames:
+              - notice
+              - warning
+              - urgent
+  ```
 
-    // ObjectCategory defines model for Object.Category.
-    type ObjectCategory int
-    ```
+  After code generation you will get this result:
+
+  ```go
+  // Defines values for ObjectCategory.
+  const (
+  	Notice  ObjectCategory = 0
+  	Urgent  ObjectCategory = 2
+  	Warning ObjectCategory = 1
+  )
+
+  // ObjectCategory defines model for Object.Category.
+  type ObjectCategory int
+  ```
 
 ## Using `oapi-codegen`
 
@@ -670,23 +695,25 @@ those via the `-generate` flag. It defaults to `types,client,server,spec`, but
 you can specify any combination of those.
 
 - `types`: generate all type definitions for all types in the OpenAPI spec. This
- will be everything under `#components`, as well as request parameter, request
- body, and response type objects.
+  will be everything under `#components`, as well as request parameter, request
+  body, and response type objects.
 - `server`: generate the Echo server boilerplate. `server` requires the types in the
- same package to compile.
+  same package to compile.
 - `chi-server`: generate the Chi server boilerplate. This code is dependent on
- that produced by the `types` target.
+  that produced by the `types` target.
+- `fiber`: generate the Fiber server boilerplate. This code is dependent
+  on that produced by the `types` target.
 - `client`: generate the client boilerplate. It, too, requires the types to be
- present in its package.
+  present in its package.
 - `spec`: embed the OpenAPI spec into the generated code as a gzipped blob.
   This is then usable with the `OapiRequestValidator`, or to be used by other
   methods that need access to the parsed OpenAPI specification
 - `skip-fmt`: skip running `goimports` on the generated code. This is useful for debugging
- the generated file in case the spec contains weird strings.
+  the generated file in case the spec contains weird strings.
 - `skip-prune`: skip pruning unused components from the spec prior to generating
- the code.
+  the code.
 - `import-mapping`: specifies a map of references external OpenAPI specs to go
- Go include paths. Please see below.
+  Go include paths. Please see below.
 
 So, for example, if you would like to produce only the server code, you could
 run `oapi-codegen -generate types,server`. You could generate `types` and
@@ -711,7 +738,7 @@ them pretty unwieldy, so you can specify all of the options in a configuration
 file via the `--config` option. Please see the test under
 [`/internal/test/externalref/`](https://github.com/deepmap/oapi-codegen/blob/master/internal/test/externalref/externalref.cfg.yaml)
 for an example. The structure of the file is as follows:
-    
+
 ```yaml
 package: externalref
 generate:
@@ -725,7 +752,7 @@ output-options:
   skip-prune: true
 ```
 
-Have a look at [`cmd/oapi-codegen/oapi-codegen.go`](https://github.com/deepmap/oapi-codegen/blob/master/cmd/oapi-codegen/oapi-codegen.go#L48) 
+Have a look at [`cmd/oapi-codegen/oapi-codegen.go`](https://github.com/deepmap/oapi-codegen/blob/master/cmd/oapi-codegen/oapi-codegen.go#L48)
 to see all the fields on the configuration structure.
 
 ### Import Mappings
@@ -756,10 +783,9 @@ This code is still young, and not complete, since we're filling it in as we
 need it. We've not yet implemented several things:
 
 - `patternProperties` isn't yet supported and will exit with an error. Pattern
- properties were defined in JSONSchema, and the `kin-openapi` Swagger object
- knows how to parse them, but they're not part of OpenAPI 3.0, so we've left
- them out, as support is very complicated.
-
+  properties were defined in JSONSchema, and the `kin-openapi` Swagger object
+  knows how to parse them, but they're not part of OpenAPI 3.0, so we've left
+  them out, as support is very complicated.
 
 ## Making changes to code generation
 
@@ -774,7 +800,7 @@ All this command does is inline the files ending in `.tmpl` into the specified
 Go file.
 
 Afterwards you should run `go generate ./...`, and the templates will be updated
- accordingly.
+accordingly.
 
 Alternatively, you can provide custom templates to override built-in ones using
 the `-templates` flag specifying a path to a directory containing templates
@@ -789,3 +815,55 @@ on-the-fly at run time. Example:
         -templates my-templates/ \
         -generate types,client \
         petstore-expanded.yaml
+
+When using the configuration file, it is possible to provide templates directly
+as text, as a local path, or as a URL. If the data provided to the
+configuration file is more than one line, the local path and URL checks will be
+ignored and will be treated as raw template text. If one line, the string
+will be used to check for a local file, followed by checking performing a HTTP
+GET request. If the file lookup returns any error other than not found, or the
+HTTP request returns a non 200 response code, the generator will error.
+
+⚠️ Warning: If using urls that tracks against git repositories such as
+`raw.githubusercontent.com`, it is strongly encouraged to use a tag or a hash
+instead of a branch like `main`. Tracking a branch can lead to unexpected API
+drift, and loss of the ability to reproduce a build.
+
+Examples:
+
+```yaml
+output: api.gen.go
+package: api
+output-options:
+  user-templates:
+    # using a local file
+    client-with-responses.tmpl: /home/username/workspace/templatesProject/my-client-with-responses.tmpl
+
+    # The following are referencing a versuion of the default
+    # client-with-responses.tmpl file, but loaded in through
+    # github's raw.githubusercontent.com. The general form
+    # to use raw.githubusercontent.com is as follows
+    # https://raw.githubusercontent.com/<username>/<project>/<hash|tag|branch>/path/to/template/template.tmpl
+
+    # using raw.githubusercontent.com with a hash
+    client-with-responses.tmpl: https://raw.githubusercontent.com/deepmap/oapi-codegen/7b010099dcf1192b3bfaa3898b5f375bb9590ddf/pkg/codegen/templates/client-with-responses.tmpl
+    # using raw.githubusercontent.com with a tag
+    client-with-responses.tmpl: https://raw.githubusercontent.com/deepmap/oapi-codegen/v1.12.4/pkg/codegen/templates/client-with-responses.tmpl
+    # using raw.githubusercontent.com with a branch
+    client-with-responses.tmpl: https://raw.githubusercontent.com/deepmap/oapi-codegen/master/pkg/codegen/templates/client-with-responses.tmpl
+
+    #This example is directly embedding the template into the config file.
+    client-with-responses.tmpl: |
+        // ClientWithResponses builds on ClientInterface to offer response payloads
+        type ClientWithResponses struct {
+            ClientInterface
+        }
+        ...
+    # template shortened for brevity
+
+```
+
+Using the configuration file to load in templates **will** load in templates
+with names other than those defined by the built in templates. These user
+templates will not be called unless the user overrides a built in template to
+call them however.
diff --git a/cmd/oapi-codegen/oapi-codegen.go b/cmd/oapi-codegen/oapi-codegen.go
index 7d9f2cc..ae81acf 100644
--- a/cmd/oapi-codegen/oapi-codegen.go
+++ b/cmd/oapi-codegen/oapi-codegen.go
@@ -53,6 +53,7 @@ var (
 	flagExcludeSchemas     string
 	flagResponseTypeSuffix string
 	flagAliasTypes         bool
+	flagInitalismOverrides bool
 )
 
 type configuration struct {
@@ -78,26 +79,27 @@ type oldConfiguration struct {
 }
 
 func main() {
-	flag.StringVar(&flagOutputFile, "o", "", "Where to output generated code, stdout is default")
-	flag.BoolVar(&flagOldConfigStyle, "old-config-style", false, "whether to use the older style config file format")
-	flag.BoolVar(&flagOutputConfig, "output-config", false, "when true, outputs a configuration file for oapi-codegen using current settings")
-	flag.StringVar(&flagConfigFile, "config", "", "a YAML config file that controls oapi-codegen behavior")
-	flag.BoolVar(&flagPrintVersion, "version", false, "when specified, print version and exit")
-	flag.StringVar(&flagPackageName, "package", "", "The package name for generated code")
-	flag.BoolVar(&flagPrintUsage, "help", false, "show this help and exit")
-	flag.BoolVar(&flagPrintUsage, "h", false, "same as -help")
+	flag.StringVar(&flagOutputFile, "o", "", "Where to output generated code, stdout is default.")
+	flag.BoolVar(&flagOldConfigStyle, "old-config-style", false, "Whether to use the older style config file format.")
+	flag.BoolVar(&flagOutputConfig, "output-config", false, "When true, outputs a configuration file for oapi-codegen using current settings.")
+	flag.StringVar(&flagConfigFile, "config", "", "A YAML config file that controls oapi-codegen behavior.")
+	flag.BoolVar(&flagPrintVersion, "version", false, "When specified, print version and exit.")
+	flag.StringVar(&flagPackageName, "package", "", "The package name for generated code.")
+	flag.BoolVar(&flagPrintUsage, "help", false, "Show this help and exit.")
+	flag.BoolVar(&flagPrintUsage, "h", false, "Same as -help.")
 
 	// All flags below are deprecated, and will be removed in a future release. Please do not
 	// update their behavior.
 	flag.StringVar(&flagGenerate, "generate", "types,client,server,spec",
-		`Comma-separated list of code to generate; valid options: "types", "client", "chi-server", "server", "gin", "gorilla", "spec", "skip-fmt", "skip-prune"`)
+		`Comma-separated list of code to generate; valid options: "types", "client", "chi-server", "server", "gin", "gorilla", "spec", "skip-fmt", "skip-prune", "fiber".`)
 	flag.StringVar(&flagIncludeTags, "include-tags", "", "Only include operations with the given tags. Comma-separated list of tags.")
 	flag.StringVar(&flagExcludeTags, "exclude-tags", "", "Exclude operations that are tagged with the given tags. Comma-separated list of tags.")
-	flag.StringVar(&flagTemplatesDir, "templates", "", "Path to directory containing user templates")
-	flag.StringVar(&flagImportMapping, "import-mapping", "", "A dict from the external reference to golang package path")
-	flag.StringVar(&flagExcludeSchemas, "exclude-schemas", "", "A comma separated list of schemas which must be excluded from generation")
-	flag.StringVar(&flagResponseTypeSuffix, "response-type-suffix", "", "the suffix used for responses types")
-	flag.BoolVar(&flagAliasTypes, "alias-types", false, "Alias type declarations of possible")
+	flag.StringVar(&flagTemplatesDir, "templates", "", "Path to directory containing user templates.")
+	flag.StringVar(&flagImportMapping, "import-mapping", "", "A dict from the external reference to golang package path.")
+	flag.StringVar(&flagExcludeSchemas, "exclude-schemas", "", "A comma separated list of schemas which must be excluded from generation.")
+	flag.StringVar(&flagResponseTypeSuffix, "response-type-suffix", "", "The suffix used for responses types.")
+	flag.BoolVar(&flagAliasTypes, "alias-types", false, "Alias type declarations of possible.")
+	flag.BoolVar(&flagInitalismOverrides, "initialism-overrides", false, "Use initialism overrides.")
 
 	flag.Parse()
 
@@ -153,7 +155,7 @@ func main() {
 			t := true
 			oldConfigStyle = &t
 		} else if oldErr != nil && newErr != nil {
-			errExit("error parsing configuration style as old version or new version: %v\n", err)
+			errExit("error parsing configuration style as old version or new version\n\nerror when parsing using old config version:\n%v\n\nerror when parsing using new config version:\n%v\n", oldErr, newErr)
 		}
 		// Else we fall through, and we still don't know, so we need to infer it from flags.
 	}
@@ -291,7 +293,7 @@ func loadTemplateOverrides(templatesDir string) (map[string]string, error) {
 	for _, f := range files {
 		// Recursively load subdirectory files, using the path relative to the templates
 		// directory as the key. This allows for overriding the files in the service-specific
-		// directories (e.g. echo, chi, etc.).
+		// directories (e.g. echo, chi, fiber, etc.).
 		if f.IsDir() {
 			subFiles, err := loadTemplateOverrides(path.Join(templatesDir, f.Name()))
 			if err != nil {
@@ -396,6 +398,8 @@ func updateConfigFromFlags(cfg *configuration) error {
 		cfg.OutputFile = flagOutputFile
 	}
 
+	cfg.OutputOptions.InitialismOverrides = flagInitalismOverrides
+
 	return nil
 }
 
@@ -441,6 +445,8 @@ func generationTargets(cfg *codegen.Configuration, targets []string) error {
 		switch opt {
 		case "chi-server", "chi":
 			opts.ChiServer = true
+		case "fiber-server", "fiber":
+			opts.FiberServer = true
 		case "server", "echo-server", "echo":
 			opts.EchoServer = true
 		case "gin", "gin-server":
diff --git a/examples/authenticated-api/echo/server/fake_jws.go b/examples/authenticated-api/echo/server/fake_jws.go
index 928b2dc..0cf44e0 100644
--- a/examples/authenticated-api/echo/server/fake_jws.go
+++ b/examples/authenticated-api/echo/server/fake_jws.go
@@ -74,7 +74,7 @@ func (f *FakeAuthenticator) ValidateJWS(jwsString string) (jwt.Token, error) {
 		jwt.WithAudience(FakeAudience), jwt.WithIssuer(FakeIssuer))
 }
 
-// SignToken takes a JWT and signs it with our priviate key, returning a JWS.
+// SignToken takes a JWT and signs it with our private key, returning a JWS.
 func (f *FakeAuthenticator) SignToken(t jwt.Token) ([]byte, error) {
 	hdr := jws.NewHeaders()
 	if err := hdr.Set(jws.AlgorithmKey, jwa.ES256); err != nil {
diff --git a/examples/authenticated-api/echo/server/jwt_authenticator.go b/examples/authenticated-api/echo/server/jwt_authenticator.go
index 4d6b334..2c1699b 100644
--- a/examples/authenticated-api/echo/server/jwt_authenticator.go
+++ b/examples/authenticated-api/echo/server/jwt_authenticator.go
@@ -7,6 +7,7 @@ import (
 	"net/http"
 	"strings"
 
+	"github.com/deepmap/oapi-codegen/pkg/middleware"
 	"github.com/getkin/kin-openapi/openapi3filter"
 	"github.com/lestrrat-go/jwx/jwt"
 )
@@ -17,6 +18,8 @@ type JWSValidator interface {
 	ValidateJWS(jws string) (jwt.Token, error)
 }
 
+const JWTClaimsContextKey = "jwt_claims"
+
 var (
 	ErrNoAuthHeader      = errors.New("Authorization header is missing")
 	ErrInvalidAuthHeader = errors.New("Authorization header is malformed")
@@ -73,6 +76,12 @@ func Authenticate(v JWSValidator, ctx context.Context, input *openapi3filter.Aut
 	if err != nil {
 		return fmt.Errorf("token claims don't match: %w", err)
 	}
+
+	// Set the property on the echo context so the handler is able to
+	// access the claims data we generate in here.
+	eCtx := middleware.GetEchoContext(ctx)
+	eCtx.Set(JWTClaimsContextKey, token)
+
 	return nil
 }
 
diff --git a/examples/petstore-expanded/fiber/api/petstore-server.gen.go b/examples/petstore-expanded/fiber/api/petstore-server.gen.go
new file mode 100644
index 0000000..a55486c
--- /dev/null
+++ b/examples/petstore-expanded/fiber/api/petstore-server.gen.go
@@ -0,0 +1,246 @@
+// Package api provides primitives to interact with the openapi HTTP API.
+//
+// Code generated by github.com/deepmap/oapi-codegen version (devel) DO NOT EDIT.
+package api
+
+import (
+	"bytes"
+	"compress/gzip"
+	"encoding/base64"
+	"fmt"
+	"net/url"
+	"path"
+	"strings"
+
+	"github.com/deepmap/oapi-codegen/pkg/runtime"
+	"github.com/getkin/kin-openapi/openapi3"
+	"github.com/gofiber/fiber/v2"
+)
+
+// ServerInterface represents all server handlers.
+type ServerInterface interface {
+	// Returns all pets
+	// (GET /pets)
+	FindPets(c *fiber.Ctx, params FindPetsParams) error
+	// Creates a new pet
+	// (POST /pets)
+	AddPet(c *fiber.Ctx) error
+	// Deletes a pet by ID
+	// (DELETE /pets/{id})
+	DeletePet(c *fiber.Ctx, id int64) error
+	// Returns a pet by ID
+	// (GET /pets/{id})
+	FindPetByID(c *fiber.Ctx, id int64) error
+}
+
+// ServerInterfaceWrapper converts contexts to parameters.
+type ServerInterfaceWrapper struct {
+	Handler ServerInterface
+}
+
+type MiddlewareFunc fiber.Handler
+
+// FindPets operation middleware
+func (siw *ServerInterfaceWrapper) FindPets(c *fiber.Ctx) error {
+
+	var err error
+
+	// Parameter object where we will unmarshal all parameters from the context
+	var params FindPetsParams
+
+	var query url.Values
+	query, err = url.ParseQuery(string(c.Request().URI().QueryString()))
+	if err != nil {
+		return fiber.NewError(fiber.StatusBadRequest, fmt.Errorf("Invalid format for query string: %w", err).Error())
+	}
+
+	// ------------- Optional query parameter "tags" -------------
+
+	err = runtime.BindQueryParameter("form", true, false, "tags", query, &params.Tags)
+	if err != nil {
+		return fiber.NewError(fiber.StatusBadRequest, fmt.Errorf("Invalid format for parameter tags: %w", err).Error())
+	}
+
+	// ------------- Optional query parameter "limit" -------------
+
+	err = runtime.BindQueryParameter("form", true, false, "limit", query, &params.Limit)
+	if err != nil {
+		return fiber.NewError(fiber.StatusBadRequest, fmt.Errorf("Invalid format for parameter limit: %w", err).Error())
+	}
+
+	return siw.Handler.FindPets(c, params)
+}
+
+// AddPet operation middleware
+func (siw *ServerInterfaceWrapper) AddPet(c *fiber.Ctx) error {
+
+	return siw.Handler.AddPet(c)
+}
+
+// DeletePet operation middleware
+func (siw *ServerInterfaceWrapper) DeletePet(c *fiber.Ctx) error {
+
+	var err error
+
+	// ------------- Path parameter "id" -------------
+	var id int64
+
+	err = runtime.BindStyledParameter("simple", false, "id", c.Params("id"), &id)
+	if err != nil {
+		return fiber.NewError(fiber.StatusBadRequest, fmt.Errorf("Invalid format for parameter id: %w", err).Error())
+	}
+
+	return siw.Handler.DeletePet(c, id)
+}
+
+// FindPetByID operation middleware
+func (siw *ServerInterfaceWrapper) FindPetByID(c *fiber.Ctx) error {
+
+	var err error
+
+	// ------------- Path parameter "id" -------------
+	var id int64
+
+	err = runtime.BindStyledParameter("simple", false, "id", c.Params("id"), &id)
+	if err != nil {
+		return fiber.NewError(fiber.StatusBadRequest, fmt.Errorf("Invalid format for parameter id: %w", err).Error())
+	}
+
+	return siw.Handler.FindPetByID(c, id)
+}
+
+// FiberServerOptions provides options for the Fiber server.
+type FiberServerOptions struct {
+	BaseURL     string
+	Middlewares []MiddlewareFunc
+}
+
+// RegisterHandlers creates http.Handler with routing matching OpenAPI spec.
+func RegisterHandlers(router fiber.Router, si ServerInterface) {
+	RegisterHandlersWithOptions(router, si, FiberServerOptions{})
+}
+
+// RegisterHandlersWithOptions creates http.Handler with additional options
+func RegisterHandlersWithOptions(router fiber.Router, si ServerInterface, options FiberServerOptions) {
+	wrapper := ServerInterfaceWrapper{
+		Handler: si,
+	}
+
+	for _, m := range options.Middlewares {
+		router.Use(m)
+	}
+
+	router.Get(options.BaseURL+"/pets", wrapper.FindPets)
+
+	router.Post(options.BaseURL+"/pets", wrapper.AddPet)
+
+	router.Delete(options.BaseURL+"/pets/:id", wrapper.DeletePet)
+
+	router.Get(options.BaseURL+"/pets/:id", wrapper.FindPetByID)
+
+}
+
+// Base64 encoded, gzipped, json marshaled Swagger object
+var swaggerSpec = []string{
+
+	"H4sIAAAAAAAC/+RXW48budH9KwV+32OnNbEXedBTvB4vICBrT+LdvKznoYZdkmrBSw9Z1FgY6L8HRbZu",
+	"I3k2QYIgQV506WY1T51zqlj9bGz0YwwUJJv5s8l2TR7rzw8pxaQ/xhRHSsJUL9s4kH4PlG3iUTgGM2+L",
+	"od7rzDImj2LmhoO8fWM6I9uR2l9aUTK7znjKGVfffND+9iE0S+KwMrtdZxI9Fk40mPkvZtpwv/x+15mP",
+	"9HRHcok7oL+y3Uf0BHEJsiYYSS437Izg6jLup+34etwLoHV3hTdhQ+c+Lc38l2fz/4mWZm7+b3YUYjap",
+	"MJty2XUvk+HhEtLPgR8LAQ/nuE7F+MN3V8R4gZQHc7+73+llDsvYJA+CtuImj+zM3ODIQuj/mJ9wtaLU",
+	"czTdRLH53K7Bu7sF/EToTWdK0qC1yJjns9lJ0K57kcU7yOhHRzVa1ihQMmVAzSZLTASYAQPQ17ZMIgzk",
+	"Y8iSUAiWhFISZeBQOfg0UtAnve1vII9keckW61adcWwpZDqaw7wb0a4J3vQ3F5ifnp56rLf7mFazKTbP",
+	"/rR4/+Hj5w+/e9Pf9GvxrjqGks+flp8pbdjS1cRndc1M5WBxp6zdTXmazmwo5cbK7/ub/kYfHUcKOLKZ",
+	"m7f1UmdGlHX1xEwZ0h+rZrFzXv9CUlLIgM5VKmGZoq8U5W0W8o1r/V8yJVgry9ZSziDxS/iIHjINYGMY",
+	"2FOQ4oGy9PAjkqWAGYT8GBNkXLEIZ8g4MoUOAllI6xhsyZDJnyxgAfQkPbyjQBgABVYJNzwgYFkV6gAt",
+	"MNriuIb28L4kfGApCeLAEVxM5DuIKWAioBUJkKMJXSDbgS0pl6wl4chKyT3cFs7gGaSkkXMHY3EbDph0",
+	"L0pRk+5AOFgeShDYYOKS4deSJfawCLBGC2sFgTkTjA6FEAa2UrzSsWhFpbngwCNny2EFGESzOebueFUc",
+	"HjIf15hIEu5J1PXgo6MsTMB+pDSwMvVX3qBvCaHjx4IeBkZlJmGGR81tQ44FQgwgMUlMSgkvKQyH3Xu4",
+	"S0iZgihMCuyPAEoKCJvoiowosKFAARVwI1c/PJakz1iE45OXlCbWl2jZcT7bpO6gH91RXws5DuhIhR06",
+	"5dFSQtHE9LuHzyWPFAZWlh2qeYboYurUgZmsqJtrltUqmnUHG1qzLQ5BW1saigfHD5RiDz/G9MBAhbOP",
+	"w6kMersa26HlwNh/CV/CZxqqEiXDktR8Lj7EVAMoHh2TiqTie9Da8FgfOJHP2XVA5axamuTgivpQ3dnD",
+	"3RozOdcKY6Q0hVeaq7wksMRi+aE0wnG/j647jd+Qm6TjDaWE3fnWWifAQ3coxMAP6x5+FhjJOQpCWU+O",
+	"MeZCWkn7IupBqcB9FWjR7bncP2mfVmWyq0AOtgglWJDEWerBtGFB6uGHki0BSe0GQ+FDFWinyJYcJa5w",
+	"mn/3AV7dUrCaxxafMYDHlaZMblKrhz+XFuqjU92aelSad45QukPzASxWi6StnOzZ0p7MMTWZQzWqWVRg",
+	"4NAdoUyFGzjzHnBWDJalDKxQc0YosvfZJGTb6Yy0ul8Pd6fCVOYmjGMi4eJPOlczTelO/K2tt/+iZ5wO",
+	"DfW8Wwxmbn7gMOj5Uo+NpARQynUKOT8sBFfa92HJTijBw9boMGDm5rFQ2h5Pel1numlorHOJkK9n0OUU",
+	"1S5gSrjV/1m29djT8aQOOOcIPH5lr228+AdKOtEkysVJhZXqWfYNTI49yxmo3xxHd/c6AuVRW0tF/+bm",
+	"Zj/3UGjz2ji6aXKY/ZoV4vO1tF8b5tok94KI3cUANJLAHkwbj5ZYnPxDeF6D0cb6KxuXQF9Hba3ag9ua",
+	"zuTiPabtlQFCsY0xXxk13idCqTNboCddux/G6lyjZ3DDrkt0nnMuPtFwYdZ3g3rVtOmUsnwfh+2/jIX9",
+	"ZH1Jwx2JegyHQb8OsM3plCyp0O6f9MxvWuW/xxoXgtf7dR6dPfOwaxZxJFdewNp1jc0cVq6+tcADapuN",
+	"zTWLW8hFc7rikdsa3Wzyakdb3GoPGZu2E5apf+gAfWwfPFwo/a1ecv1t6rKXfHeZtQJpKIb/JCFvD2JU",
+	"FbawuFV4r79QnCt20HFx+63j5/ttvff367Ukset/m1z/s2X8QtGmfl1CabOX6fyteP9S3p+82err6e5+",
+	"97cAAAD//ykDnxlaEgAA",
+}
+
+// GetSwagger returns the content of the embedded swagger specification file
+// or error if failed to decode
+func decodeSpec() ([]byte, error) {
+	zipped, err := base64.StdEncoding.DecodeString(strings.Join(swaggerSpec, ""))
+	if err != nil {
+		return nil, fmt.Errorf("error base64 decoding spec: %s", err)
+	}
+	zr, err := gzip.NewReader(bytes.NewReader(zipped))
+	if err != nil {
+		return nil, fmt.Errorf("error decompressing spec: %s", err)
+	}
+	var buf bytes.Buffer
+	_, err = buf.ReadFrom(zr)
+	if err != nil {
+		return nil, fmt.Errorf("error decompressing spec: %s", err)
+	}
+
+	return buf.Bytes(), nil
+}
+
+var rawSpec = decodeSpecCached()
+
+// a naive cached of a decoded swagger spec
+func decodeSpecCached() func() ([]byte, error) {
+	data, err := decodeSpec()
+	return func() ([]byte, error) {
+		return data, err
+	}
+}
+
+// Constructs a synthetic filesystem for resolving external references when loading openapi specifications.
+func PathToRawSpec(pathToFile string) map[string]func() ([]byte, error) {
+	var res = make(map[string]func() ([]byte, error))
+	if len(pathToFile) > 0 {
+		res[pathToFile] = rawSpec
+	}
+
+	return res
+}
+
+// GetSwagger returns the Swagger specification corresponding to the generated code
+// in this file. The external references of Swagger specification are resolved.
+// The logic of resolving external references is tightly connected to "import-mapping" feature.
+// Externally referenced files must be embedded in the corresponding golang packages.
+// Urls can be supported but this task was out of the scope.
+func GetSwagger() (swagger *openapi3.T, err error) {
+	var resolvePath = PathToRawSpec("")
+
+	loader := openapi3.NewLoader()
+	loader.IsExternalRefsAllowed = true
+	loader.ReadFromURIFunc = func(loader *openapi3.Loader, url *url.URL) ([]byte, error) {
+		var pathToFile = url.String()
+		pathToFile = path.Clean(pathToFile)
+		getSpec, ok := resolvePath[pathToFile]
+		if !ok {
+			err1 := fmt.Errorf("path not found: %s", pathToFile)
+			return nil, err1
+		}
+		return getSpec()
+	}
+	var specData []byte
+	specData, err = rawSpec()
+	if err != nil {
+		return
+	}
+	swagger, err = loader.LoadFromData(specData)
+	if err != nil {
+		return
+	}
+	return
+}
diff --git a/examples/petstore-expanded/fiber/api/petstore-types.gen.go b/examples/petstore-expanded/fiber/api/petstore-types.gen.go
new file mode 100644
index 0000000..16bbe20
--- /dev/null
+++ b/examples/petstore-expanded/fiber/api/petstore-types.gen.go
@@ -0,0 +1,46 @@
+// Package api provides primitives to interact with the openapi HTTP API.
+//
+// Code generated by github.com/deepmap/oapi-codegen version (devel) DO NOT EDIT.
+package api
+
+// Error defines model for Error.
+type Error struct {
+	// Code Error code
+	Code int32 `json:"code"`
+
+	// Message Error message
+	Message string `json:"message"`
+}
+
+// NewPet defines model for NewPet.
+type NewPet struct {
+	// Name Name of the pet
+	Name string `json:"name"`
+
+	// Tag Type of the pet
+	Tag *string `json:"tag,omitempty"`
+}
+
+// Pet defines model for Pet.
+type Pet struct {
+	// Id Unique id of the pet
+	Id int64 `json:"id"`
+
+	// Name Name of the pet
+	Name string `json:"name"`
+
+	// Tag Type of the pet
+	Tag *string `json:"tag,omitempty"`
+}
+
+// FindPetsParams defines parameters for FindPets.
+type FindPetsParams struct {
+	// Tags tags to filter by
+	Tags *[]string `form:"tags,omitempty" json:"tags,omitempty"`
+
+	// Limit maximum number of results to return
+	Limit *int32 `form:"limit,omitempty" json:"limit,omitempty"`
+}
+
+// AddPetJSONRequestBody defines body for AddPet for application/json ContentType.
+type AddPetJSONRequestBody = NewPet
diff --git a/examples/petstore-expanded/fiber/api/petstore.go b/examples/petstore-expanded/fiber/api/petstore.go
new file mode 100644
index 0000000..60ecd58
--- /dev/null
+++ b/examples/petstore-expanded/fiber/api/petstore.go
@@ -0,0 +1,132 @@
+//go:generate go run github.com/deepmap/oapi-codegen/cmd/oapi-codegen --config=types.cfg.yaml ../../petstore-expanded.yaml
+//go:generate go run github.com/deepmap/oapi-codegen/cmd/oapi-codegen --config=server.cfg.yaml ../../petstore-expanded.yaml
+
+package api
+
+import (
+	"fmt"
+	"net/http"
+	"sync"
+
+	"github.com/gofiber/fiber/v2"
+)
+
+type PetStore struct {
+	Pets   map[int64]Pet
+	NextId int64
+	Lock   sync.Mutex
+}
+
+// Make sure we conform to ServerInterface
+
+var _ ServerInterface = (*PetStore)(nil)
+
+func NewPetStore() *PetStore {
+
+	return &PetStore{
+		Pets:   make(map[int64]Pet),
+		NextId: 1000,
+	}
+}
+
+// This function wraps sending of an error in the Error format, and
+// handling the failure to marshal that.
+func sendPetStoreError(c *fiber.Ctx, code int, message string) error {
+
+	petErr := Error{
+		Code:    int32(code),
+		Message: message,
+	}
+
+	return c.Status(code).JSON(petErr)
+}
+
+// FindPets implements all the handlers in the ServerInterface
+func (p *PetStore) FindPets(c *fiber.Ctx, params FindPetsParams) error {
+
+	p.Lock.Lock()
+	defer p.Lock.Unlock()
+
+	var result []Pet
+
+	for _, pet := range p.Pets {
+		if params.Tags != nil {
+			// If we have tags,  filter pets by tag
+			for _, t := range *params.Tags {
+				if pet.Tag != nil && (*pet.Tag == t) {
+					result = append(result, pet)
+				}
+			}
+		} else {
+			// Add all pets if we're not filtering
+			result = append(result, pet)
+		}
+
+		if params.Limit != nil {
+			l := int(*params.Limit)
+			if len(result) >= l {
+				// We're at the limit
+				break
+			}
+		}
+	}
+
+	return c.Status(http.StatusOK).JSON(result)
+}
+
+func (p *PetStore) AddPet(c *fiber.Ctx) error {
+
+	// We expect a NewPet object in the request body.
+	var newPet NewPet
+
+	if err := c.BodyParser(&newPet); err != nil {
+		return sendPetStoreError(c, http.StatusBadRequest, "Invalid format for NewPet")
+	}
+
+	// We now have a pet, let's add it to our "database".
+
+	// We're always asynchronous, so lock unsafe operations below
+	p.Lock.Lock()
+	defer p.Lock.Unlock()
+
+	// We handle pets, not NewPets, which have an additional ID field
+	var pet Pet
+	pet.Name = newPet.Name
+	pet.Tag = newPet.Tag
+	pet.Id = p.NextId
+	p.NextId = p.NextId + 1
+
+	// Insert into map
+	p.Pets[pet.Id] = pet
+
+	// Now, we have to return the NewPet
+	return c.Status(http.StatusCreated).JSON(pet)
+}
+
+func (p *PetStore) FindPetByID(c *fiber.Ctx, id int64) error {
+
+	p.Lock.Lock()
+	defer p.Lock.Unlock()
+
+	pet, found := p.Pets[id]
+	if !found {
+		return sendPetStoreError(c, http.StatusNotFound, fmt.Sprintf("Could not find pet with ID %d", id))
+	}
+
+	return c.Status(http.StatusOK).JSON(pet)
+}
+
+func (p *PetStore) DeletePet(c *fiber.Ctx, id int64) error {
+
+	p.Lock.Lock()
+	defer p.Lock.Unlock()
+
+	_, found := p.Pets[id]
+	if !found {
+		return sendPetStoreError(c, http.StatusNotFound, fmt.Sprintf("Could not find pet with ID %d", id))
+	}
+	delete(p.Pets, id)
+
+	c.Status(http.StatusNoContent)
+	return nil
+}
diff --git a/examples/petstore-expanded/fiber/api/server.cfg.yaml b/examples/petstore-expanded/fiber/api/server.cfg.yaml
new file mode 100644
index 0000000..91ef133
--- /dev/null
+++ b/examples/petstore-expanded/fiber/api/server.cfg.yaml
@@ -0,0 +1,5 @@
+package: api
+generate:
+  fiber-server: true
+  embedded-spec: true
+output: petstore-server.gen.go
diff --git a/examples/petstore-expanded/fiber/api/types.cfg.yaml b/examples/petstore-expanded/fiber/api/types.cfg.yaml
new file mode 100644
index 0000000..9ac30e1
--- /dev/null
+++ b/examples/petstore-expanded/fiber/api/types.cfg.yaml
@@ -0,0 +1,4 @@
+package: api
+generate:
+  models: true
+output: petstore-types.gen.go
diff --git a/examples/petstore-expanded/fiber/petstore.go b/examples/petstore-expanded/fiber/petstore.go
new file mode 100644
index 0000000..344752b
--- /dev/null
+++ b/examples/petstore-expanded/fiber/petstore.go
@@ -0,0 +1,57 @@
+// This is an example of implementing the Pet Store from the OpenAPI documentation
+// found at:
+// https://github.com/OAI/OpenAPI-Specification/blob/master/examples/v3.0/petstore.yaml
+
+package main
+
+import (
+	"flag"
+	"fmt"
+	"log"
+	"os"
+
+	"github.com/gofiber/fiber/v2"
+
+	"github.com/deepmap/oapi-codegen/examples/petstore-expanded/fiber/api"
+	middleware "github.com/deepmap/oapi-codegen/pkg/fiber-middleware"
+)
+
+func main() {
+
+	var port = flag.Int("port", 8080, "Port for test HTTP server")
+
+	flag.Parse()
+
+	// Create an instance of our handler which satisfies the generated interface
+	petStore := api.NewPetStore()
+
+	s := NewFiberPetServer(petStore)
+
+	// And we serve HTTP until the world ends.
+	log.Fatal(s.Listen(fmt.Sprintf("localhost:%d", *port)))
+}
+
+func NewFiberPetServer(petStore *api.PetStore) *fiber.App {
+
+	swagger, err := api.GetSwagger()
+	if err != nil {
+		fmt.Fprintf(os.Stderr, "Error loading swagger spec\n: %s", err)
+		os.Exit(1)
+	}
+
+	// Clear out the servers array in the swagger spec, that skips validating
+	// that server names match. We don't know how this thing will be run.
+	swagger.Servers = nil
+
+	// This is how you set up a basic fiber router
+	app := fiber.New()
+
+	// Use our validation middleware to check all requests against the
+	// OpenAPI schema.
+	app.Use(middleware.OapiRequestValidator(swagger))
+
+	// We now register our petStore above as the handler for the interface
+	api.RegisterHandlers(app, petStore)
+
+	return app
+}
diff --git a/examples/petstore-expanded/petstore-client.gen.go b/examples/petstore-expanded/petstore-client.gen.go
index 0929a64..766135e 100644
--- a/examples/petstore-expanded/petstore-client.gen.go
+++ b/examples/petstore-expanded/petstore-client.gen.go
@@ -225,42 +225,44 @@ func NewFindPetsRequest(server string, params *FindPetsParams) (*http.Request, e
 		return nil, err
 	}
 
-	queryValues := queryURL.Query()
-
-	if params.Tags != nil {
-
-		if queryFrag, err := runtime.StyleParamWithLocation("form", true, "tags", runtime.ParamLocationQuery, *params.Tags); err != nil {
-			return nil, err
-		} else if parsed, err := url.ParseQuery(queryFrag); err != nil {
-			return nil, err
-		} else {
-			for k, v := range parsed {
-				for _, v2 := range v {
-					queryValues.Add(k, v2)
+	if params != nil {
+		queryValues := queryURL.Query()
+
+		if params.Tags != nil {
+
+			if queryFrag, err := runtime.StyleParamWithLocation("form", true, "tags", runtime.ParamLocationQuery, *params.Tags); err != nil {
+				return nil, err
+			} else if parsed, err := url.ParseQuery(queryFrag); err != nil {
+				return nil, err
+			} else {
+				for k, v := range parsed {
+					for _, v2 := range v {
+						queryValues.Add(k, v2)
+					}
 				}
 			}
-		}
-
-	}
 
-	if params.Limit != nil {
+		}
 
-		if queryFrag, err := runtime.StyleParamWithLocation("form", true, "limit", runtime.ParamLocationQuery, *params.Limit); err != nil {
-			return nil, err
-		} else if parsed, err := url.ParseQuery(queryFrag); err != nil {
-			return nil, err
-		} else {
-			for k, v := range parsed {
-				for _, v2 := range v {
-					queryValues.Add(k, v2)
+		if params.Limit != nil {
+
+			if queryFrag, err := runtime.StyleParamWithLocation("form", true, "limit", runtime.ParamLocationQuery, *params.Limit); err != nil {
+				return nil, err
+			} else if parsed, err := url.ParseQuery(queryFrag); err != nil {
+				return nil, err
+			} else {
+				for k, v := range parsed {
+					for _, v2 := range v {
+						queryValues.Add(k, v2)
+					}
 				}
 			}
+
 		}
 
+		queryURL.RawQuery = queryValues.Encode()
 	}
 
-	queryURL.RawQuery = queryValues.Encode()
-
 	req, err := http.NewRequest("GET", queryURL.String(), nil)
 	if err != nil {
 		return nil, err
diff --git a/examples/petstore-expanded/strict/api/petstore-server.gen.go b/examples/petstore-expanded/strict/api/petstore-server.gen.go
index cd9352a..822dee7 100644
--- a/examples/petstore-expanded/strict/api/petstore-server.gen.go
+++ b/examples/petstore-expanded/strict/api/petstore-server.gen.go
@@ -408,9 +408,8 @@ type StrictServerInterface interface {
 	FindPetByID(ctx context.Context, request FindPetByIDRequestObject) (FindPetByIDResponseObject, error)
 }
 
-type StrictHandlerFunc func(ctx context.Context, w http.ResponseWriter, r *http.Request, args interface{}) (interface{}, error)
-
-type StrictMiddlewareFunc func(f StrictHandlerFunc, operationID string) StrictHandlerFunc
+type StrictHandlerFunc = runtime.StrictHttpHandlerFunc
+type StrictMiddlewareFunc = runtime.StrictHttpMiddlewareFunc
 
 type StrictHTTPServerOptions struct {
 	RequestErrorHandlerFunc  func(w http.ResponseWriter, r *http.Request, err error)
diff --git a/go.mod b/go.mod
index 31bc950..aeff31d 100644
--- a/go.mod
+++ b/go.mod
@@ -2,40 +2,44 @@ module github.com/deepmap/oapi-codegen
 
 require (
 	github.com/apapsch/go-jsonmerge/v2 v2.0.0
-	github.com/getkin/kin-openapi v0.115.0
-	github.com/gin-gonic/gin v1.9.0
+	github.com/getkin/kin-openapi v0.117.0
+	github.com/gin-gonic/gin v1.9.1
 	github.com/go-chi/chi/v5 v5.0.8
+	github.com/gofiber/fiber/v2 v2.46.0
 	github.com/golangci/lint-1 v0.0.0-20181222135242-d2cdd8c08219
 	github.com/google/uuid v1.3.0
 	github.com/gorilla/mux v1.8.0
 	github.com/labstack/echo/v4 v4.10.2
 	github.com/lestrrat-go/jwx v1.2.25
 	github.com/matryer/moq v0.3.1
-	github.com/stretchr/testify v1.8.2
-	golang.org/x/text v0.8.0
-	golang.org/x/tools v0.7.0
+	github.com/stretchr/testify v1.8.3
+	golang.org/x/text v0.9.0
+	golang.org/x/tools v0.9.2
 	gopkg.in/yaml.v2 v2.4.0
 )
 
 require (
-	github.com/bytedance/sonic v1.8.0 // indirect
+	github.com/andybalholm/brotli v1.0.5 // indirect
+	github.com/bytedance/sonic v1.9.1 // indirect
 	github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
 	github.com/davecgh/go-spew v1.1.1 // indirect
 	github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect
+	github.com/gabriel-vasile/mimetype v1.4.2 // indirect
 	github.com/gin-contrib/sse v0.1.0 // indirect
 	github.com/go-openapi/jsonpointer v0.19.5 // indirect
 	github.com/go-openapi/swag v0.21.1 // indirect
 	github.com/go-playground/locales v0.14.1 // indirect
 	github.com/go-playground/universal-translator v0.18.1 // indirect
-	github.com/go-playground/validator/v10 v10.11.2 // indirect
-	github.com/goccy/go-json v0.10.0 // indirect
+	github.com/go-playground/validator/v10 v10.14.0 // indirect
+	github.com/goccy/go-json v0.10.2 // indirect
 	github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
 	github.com/invopop/yaml v0.1.0 // indirect
 	github.com/josharian/intern v1.0.0 // indirect
 	github.com/json-iterator/go v1.1.12 // indirect
-	github.com/klauspost/cpuid/v2 v2.0.9 // indirect
+	github.com/klauspost/compress v1.16.3 // indirect
+	github.com/klauspost/cpuid/v2 v2.2.4 // indirect
 	github.com/labstack/gommon v0.4.0 // indirect
-	github.com/leodido/go-urn v1.2.1 // indirect
+	github.com/leodido/go-urn v1.2.4 // indirect
 	github.com/lestrrat-go/backoff/v2 v2.0.8 // indirect
 	github.com/lestrrat-go/blackmagic v1.0.0 // indirect
 	github.com/lestrrat-go/httpcc v1.0.1 // indirect
@@ -43,27 +47,35 @@ require (
 	github.com/lestrrat-go/option v1.0.0 // indirect
 	github.com/mailru/easyjson v0.7.7 // indirect
 	github.com/mattn/go-colorable v0.1.13 // indirect
-	github.com/mattn/go-isatty v0.0.17 // indirect
+	github.com/mattn/go-isatty v0.0.19 // indirect
+	github.com/mattn/go-runewidth v0.0.14 // indirect
 	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
 	github.com/modern-go/reflect2 v1.0.2 // indirect
 	github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
-	github.com/pelletier/go-toml/v2 v2.0.6 // indirect
+	github.com/pelletier/go-toml/v2 v2.0.8 // indirect
 	github.com/perimeterx/marshmallow v1.1.4 // indirect
+	github.com/philhofer/fwd v1.1.2 // indirect
 	github.com/pkg/errors v0.9.1 // indirect
 	github.com/pmezard/go-difflib v1.0.0 // indirect
+	github.com/rivo/uniseg v0.2.0 // indirect
+	github.com/savsgio/dictpool v0.0.0-20221023140959-7bf2e61cea94 // indirect
+	github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee // indirect
 	github.com/stretchr/objx v0.5.0 // indirect
+	github.com/tinylib/msgp v1.1.8 // indirect
 	github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
-	github.com/ugorji/go/codec v1.2.9 // indirect
+	github.com/ugorji/go/codec v1.2.11 // indirect
 	github.com/valyala/bytebufferpool v1.0.0 // indirect
+	github.com/valyala/fasthttp v1.47.0 // indirect
 	github.com/valyala/fasttemplate v1.2.2 // indirect
-	golang.org/x/arch v0.0.0-20210923205945-b76863e36670 // indirect
-	golang.org/x/crypto v0.6.0 // indirect
-	golang.org/x/mod v0.9.0 // indirect
-	golang.org/x/net v0.8.0 // indirect
-	golang.org/x/sys v0.6.0 // indirect
+	github.com/valyala/tcplisten v1.0.0 // indirect
+	golang.org/x/arch v0.3.0 // indirect
+	golang.org/x/crypto v0.9.0 // indirect
+	golang.org/x/mod v0.10.0 // indirect
+	golang.org/x/net v0.10.0 // indirect
+	golang.org/x/sys v0.8.0 // indirect
 	golang.org/x/time v0.3.0 // indirect
 	golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f // indirect
-	google.golang.org/protobuf v1.28.1 // indirect
+	google.golang.org/protobuf v1.30.0 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
 )
 
diff --git a/go.sum b/go.sum
index e2d021d..f11b870 100644
--- a/go.sum
+++ b/go.sum
@@ -1,10 +1,12 @@
 github.com/RaveNoX/go-jsoncommentstrip v1.0.0/go.mod h1:78ihd09MekBnJnxpICcwzCMzGrKSKYe4AqU6PDYYpjk=
+github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
+github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
 github.com/apapsch/go-jsonmerge/v2 v2.0.0 h1:axGnT1gRIfimI7gJifB699GoE/oq+F2MU7Dml6nw9rQ=
 github.com/apapsch/go-jsonmerge/v2 v2.0.0/go.mod h1:lvDnEdqiQrp0O42VQGgmlKpxL1AP2+08jFMw88y4klk=
 github.com/bmatcuk/doublestar v1.1.1/go.mod h1:UD6OnuiIn0yFxxA2le/rnRU1G4RaI4UvFv1sNto9p6w=
 github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
-github.com/bytedance/sonic v1.8.0 h1:ea0Xadu+sHlu7x5O3gKhRpQ1IKiMrSiHttPF0ybECuA=
-github.com/bytedance/sonic v1.8.0/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
+github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s=
+github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
 github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
 github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams=
 github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
@@ -16,12 +18,14 @@ github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn
 github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.0-20210816181553-5444fa50b93d/go.mod h1:tmAIfUFEirG/Y8jhZ9M+h36obRZAk/1fcSpXwAVlfqE=
 github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1m5sE92cU+pd5Mcc=
 github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs=
-github.com/getkin/kin-openapi v0.115.0 h1:c8WHRLVY3G8m9jQTy0/DnIuljgRwTCB5twZytQS4JyU=
-github.com/getkin/kin-openapi v0.115.0/go.mod h1:l5e9PaFUo9fyLJCPGQeXI2ML8c3P8BHOEV2VaAVf/pc=
+github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
+github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
+github.com/getkin/kin-openapi v0.117.0 h1:QT2DyGujAL09F4NrKDHJGsUoIprlIcFVHWDVDcUFE8A=
+github.com/getkin/kin-openapi v0.117.0/go.mod h1:l5e9PaFUo9fyLJCPGQeXI2ML8c3P8BHOEV2VaAVf/pc=
 github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
 github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
-github.com/gin-gonic/gin v1.9.0 h1:OjyFBKICoexlu99ctXNR2gg+c5pKrKMuyjgARg9qeY8=
-github.com/gin-gonic/gin v1.9.0/go.mod h1:W1Me9+hsUSyj3CePGrd1/QrKJMSJ1Tu/0hFEH89961k=
+github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
+github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
 github.com/go-chi/chi/v5 v5.0.8 h1:lD+NLqFcAi1ovnVZpsnObHGW4xb4J8lNmoYVfECH1Y0=
 github.com/go-chi/chi/v5 v5.0.8/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
 github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY=
@@ -34,13 +38,15 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o
 github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
 github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
 github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
-github.com/go-playground/validator/v10 v10.11.2 h1:q3SHpufmypg+erIExEKUmsgmhDTyhcJ38oeKGACXohU=
-github.com/go-playground/validator/v10 v10.11.2/go.mod h1:NieE624vt4SCTJtD87arVLvdmjPAeV8BQlHtMnw9D7s=
+github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js=
+github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
 github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM=
 github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
 github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
-github.com/goccy/go-json v0.10.0 h1:mXKd9Qw4NuzShiRlOXKews24ufknHO7gx30lsDyokKA=
-github.com/goccy/go-json v0.10.0/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
+github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
+github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
+github.com/gofiber/fiber/v2 v2.46.0 h1:wkkWotblsGVlLjXj2dpgKQAYHtXumsK/HyFugQM68Ns=
+github.com/gofiber/fiber/v2 v2.46.0/go.mod h1:DNl0/c37WLe0g92U6lx1VMQuxGUQY5V7EIaVoEsUffc=
 github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
 github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
 github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
@@ -60,10 +66,12 @@ github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFF
 github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
 github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
 github.com/juju/gnuflag v0.0.0-20171113085948-2ce1bb71843d/go.mod h1:2PavIy+JPciBPrBUjwbNvtwB6RQlve+hkpll6QSNmOE=
-github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4=
+github.com/klauspost/compress v1.16.3 h1:XuJt9zzcnaz6a16/OU53ZjWp/v7/42WcR5t2a0PcNQY=
+github.com/klauspost/compress v1.16.3/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
 github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
+github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk=
+github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
 github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
-github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
 github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
 github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
 github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
@@ -72,8 +80,8 @@ github.com/labstack/echo/v4 v4.10.2 h1:n1jAhnq/elIFTHr1EYpiYtyKgx4RW9ccVgkqByZaN
 github.com/labstack/echo/v4 v4.10.2/go.mod h1:OEyqf2//K1DFdE57vw2DRgWY0M7s65IVQO2FzvI4J5k=
 github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8=
 github.com/labstack/gommon v0.4.0/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM=
-github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
-github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
+github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
+github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
 github.com/lestrrat-go/backoff/v2 v2.0.8 h1:oNb5E5isby2kiro9AgdHLv5N5tint1AnDVVf2E2un5A=
 github.com/lestrrat-go/backoff/v2 v2.0.8/go.mod h1:rHP/q/r9aT27n24JQLa7JhSQZCKBBOiM/uP402WwN8Y=
 github.com/lestrrat-go/blackmagic v1.0.0 h1:XzdxDbuQTz0RZZEmdU7cnQxUtFUzgCSPq8RCz4BxIi4=
@@ -98,8 +106,10 @@ github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxec
 github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
 github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
 github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
-github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
-github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
+github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
+github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
+github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
+github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
 github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
 github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
 github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@@ -107,16 +117,26 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G
 github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
 github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
 github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
+github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
 github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
-github.com/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvIwycIU=
-github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek=
+github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ=
+github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4=
 github.com/perimeterx/marshmallow v1.1.4 h1:pZLDH9RjlLGGorbXhcaQLhfuV0pFMNfPO55FuFkxqLw=
 github.com/perimeterx/marshmallow v1.1.4/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw=
+github.com/philhofer/fwd v1.1.1/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU=
+github.com/philhofer/fwd v1.1.2 h1:bnDivRJ1EWPjUIRXV5KfORO897HTbpFAQddBdE8t7Gw=
+github.com/philhofer/fwd v1.1.2/go.mod h1:qkPdfjR2SIEbspLqpe1tO4n5yICnr2DY7mqEx2tUTP0=
 github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
 github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
+github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
+github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
+github.com/savsgio/dictpool v0.0.0-20221023140959-7bf2e61cea94 h1:rmMl4fXJhKMNWl+K+r/fq4FbbKI+Ia2m9hYBLm2h4G4=
+github.com/savsgio/dictpool v0.0.0-20221023140959-7bf2e61cea94/go.mod h1:90zrgN3D/WJsDd1iXHT96alCoN2KJo6/4x1DZC3wZs8=
+github.com/savsgio/gotils v0.0.0-20220530130905-52f3993e8d6d/go.mod h1:Gy+0tqhJvgGlqnTF8CVGP0AaGRjwBtXs/a5PA0Y3+A4=
+github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee h1:8Iv5m6xEo1NR1AvpV+7XmhI4r39LGNzwUL4YpMuL5vk=
+github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee/go.mod h1:qwtSXrKuJh/zsFQ12yEE89xfCrGKK63Rr7ctU/uCo4g=
 github.com/spkg/bom v0.0.0-20160624110644-59b7046e48ad/go.mod h1:qLr4V1qq6nMqFKkMo8ZTx3f+BZEkzsRUY10Xsm2mwU0=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
@@ -129,58 +149,107 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
 github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
 github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
-github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
 github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
+github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY=
+github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
+github.com/tinylib/msgp v1.1.6/go.mod h1:75BAfg2hauQhs3qedfdDZmWAPcFMAvJE5b9rGOMufyw=
+github.com/tinylib/msgp v1.1.8 h1:FCXC1xanKO4I8plpHGH2P7koL/RzZs12l/+r7vakfm0=
+github.com/tinylib/msgp v1.1.8/go.mod h1:qkpG+2ldGg4xRFmx+jfTvZPxfGFhi64BcnL9vkCm/Tw=
 github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
 github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
 github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M=
 github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
-github.com/ugorji/go/codec v1.2.9 h1:rmenucSohSTiyL09Y+l2OCk+FrMxGMzho2+tjr5ticU=
-github.com/ugorji/go/codec v1.2.9/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
+github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
+github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
 github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
 github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
+github.com/valyala/fasthttp v1.47.0 h1:y7moDoxYzMooFpT5aHgNgVOQDrS3qlkfiP9mDtGGK9c=
+github.com/valyala/fasthttp v1.47.0/go.mod h1:k2zXd82h/7UZc3VOdJ2WaUqt1uZ/XpXAfE9i+HBC3lA=
 github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
 github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
 github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
-golang.org/x/arch v0.0.0-20210923205945-b76863e36670 h1:18EFjUmQOcUvxNYSkA6jO9VAiXCnxFY6NyDX0bHDmkU=
+github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8=
+github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
+github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
 golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
+golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k=
+golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
-golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc=
-golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
-golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs=
-golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
+golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g=
+golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
+golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
+golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
+golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk=
+golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
 golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
-golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
-golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
+golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
+golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
+golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
+golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
+golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
+golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
+golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
-golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
+golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
+golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
+golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
+golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
 golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
 golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4=
-golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20201022035929-9cf592e881e9/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
+golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ=
+golang.org/x/tools v0.9.2 h1:UXbndbirwCAx6TULftIfie/ygDNCwxEie+IiNP1IcNc=
+golang.org/x/tools v0.9.2/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f h1:GGU+dLjvlC3qDwqYgL6UgRmHXhOOgns0bZu2Ty5mm6U=
 golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
-google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
-google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
+google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
+google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
 gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
 gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
 gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
diff --git a/pkg/chi-middleware/oapi_validate.go b/pkg/chi-middleware/oapi_validate.go
index c09c2f1..b654dc3 100644
--- a/pkg/chi-middleware/oapi_validate.go
+++ b/pkg/chi-middleware/oapi_validate.go
@@ -1,6 +1,6 @@
 // Package middleware implements middleware function for go-chi or net/http,
 // which validates incoming HTTP requests to make sure that they conform to the given OAPI 3.0 specification.
-// When OAPI validation failes on the request, we return an HTTP/400.
+// When OAPI validation fails on the request, we return an HTTP/400.
 package middleware
 
 import (
diff --git a/pkg/codegen/codegen.go b/pkg/codegen/codegen.go
index 956d589..6cb5fec 100644
--- a/pkg/codegen/codegen.go
+++ b/pkg/codegen/codegen.go
@@ -19,7 +19,10 @@ import (
 	"bytes"
 	"embed"
 	"fmt"
+	"io"
 	"io/fs"
+	"net/http"
+	"os"
 	"runtime/debug"
 	"sort"
 	"strings"
@@ -37,8 +40,9 @@ var templates embed.FS
 // globalState stores all global state. Please don't put global state anywhere
 // else so that we can easily track it.
 var globalState struct {
-	options Configuration
-	spec    *openapi3.T
+	options       Configuration
+	spec          *openapi3.T
+	importMapping importMap
 }
 
 // goImport represents a go package to be imported in the generated code
@@ -67,8 +71,6 @@ func (im importMap) GoImports() []string {
 	return goImports
 }
 
-var importMapping importMap
-
 func constructImportMapping(importMapping map[string]string) importMap {
 	var (
 		pathToName = map[string]string{}
@@ -101,8 +103,7 @@ func Generate(spec *openapi3.T, opts Configuration) (string, error) {
 	// This is global state
 	globalState.options = opts
 	globalState.spec = spec
-
-	importMapping = constructImportMapping(opts.ImportMapping)
+	globalState.importMapping = constructImportMapping(opts.ImportMapping)
 
 	filterOperationsByTag(spec, opts)
 	if !opts.OutputOptions.SkipPrune {
@@ -128,17 +129,22 @@ func Generate(spec *openapi3.T, opts Configuration) (string, error) {
 		return "", fmt.Errorf("error parsing oapi-codegen templates: %w", err)
 	}
 
-	// Override built-in templates with user-provided versions
-	for _, tpl := range t.Templates() {
-		if _, ok := opts.OutputOptions.UserTemplates[tpl.Name()]; ok {
-			utpl := t.New(tpl.Name())
-			if _, err := utpl.Parse(opts.OutputOptions.UserTemplates[tpl.Name()]); err != nil {
-				return "", fmt.Errorf("error parsing user-provided template %q: %w", tpl.Name(), err)
-			}
+	// load user-provided templates. Will Override built-in versions.
+	for name, template := range opts.OutputOptions.UserTemplates {
+		utpl := t.New(name)
+
+		txt, err := GetUserTemplateText(template)
+		if err != nil {
+			return "", fmt.Errorf("error loading user-provided template %q: %w", name, err)
+		}
+
+		_, err = utpl.Parse(txt)
+		if err != nil {
+			return "", fmt.Errorf("error parsing user-provided template %q: %w", name, err)
 		}
 	}
 
-	ops, err := OperationDefinitions(spec)
+	ops, err := OperationDefinitions(spec, opts.OutputOptions.InitialismOverrides)
 	if err != nil {
 		return "", fmt.Errorf("error creating operation definitions: %w", err)
 	}
@@ -183,6 +189,14 @@ func Generate(spec *openapi3.T, opts Configuration) (string, error) {
 		}
 	}
 
+	var fiberServerOut string
+	if opts.Generate.FiberServer {
+		fiberServerOut, err = GenerateFiberServer(t, ops)
+		if err != nil {
+			return "", fmt.Errorf("error generating Go handlers for Paths: %w", err)
+		}
+	}
+
 	var ginServerOut string
 	if opts.Generate.GinServer {
 		ginServerOut, err = GenerateGinServer(t, ops)
@@ -237,7 +251,7 @@ func Generate(spec *openapi3.T, opts Configuration) (string, error) {
 
 	var inlinedSpec string
 	if opts.Generate.EmbeddedSpec {
-		inlinedSpec, err = GenerateInlinedSpec(t, importMapping, spec)
+		inlinedSpec, err = GenerateInlinedSpec(t, globalState.importMapping, spec)
 		if err != nil {
 			return "", fmt.Errorf("error generating Go handlers for Paths: %w", err)
 		}
@@ -246,7 +260,7 @@ func Generate(spec *openapi3.T, opts Configuration) (string, error) {
 	var buf bytes.Buffer
 	w := bufio.NewWriter(&buf)
 
-	externalImports := append(importMapping.GoImports(), importMap(xGoTypeImports).GoImports()...)
+	externalImports := append(globalState.importMapping.GoImports(), importMap(xGoTypeImports).GoImports()...)
 	importsOut, err := GenerateImports(t, externalImports, opts.PackageName)
 	if err != nil {
 		return "", fmt.Errorf("error generating imports: %w", err)
@@ -292,6 +306,13 @@ func Generate(spec *openapi3.T, opts Configuration) (string, error) {
 		}
 	}
 
+	if opts.Generate.FiberServer {
+		_, err = w.WriteString(fiberServerOut)
+		if err != nil {
+			return "", fmt.Errorf("error writing server path handlers: %w", err)
+		}
+	}
+
 	if opts.Generate.GinServer {
 		_, err = w.WriteString(ginServerOut)
 		if err != nil {
@@ -814,7 +835,45 @@ func GenerateUnionAndAdditionalProopertiesBoilerplate(t *template.Template, type
 func SanitizeCode(goCode string) string {
 	// remove any byte-order-marks which break Go-Code
 	// See: https://groups.google.com/forum/#!topic/golang-nuts/OToNIPdfkks
-	return strings.Replace(goCode, "\uFEFF", "", -1)
+	return strings.ReplaceAll(goCode, "\uFEFF", "")
+}
+
+// GetUserTemplateText attempts to retrieve the template text from a passed in URL or file
+// path when inputData is more than one line.
+// This function will attempt to load a file first, and if it fails, will try to get the
+// data from the remote endpoint.
+func GetUserTemplateText(inputData string) (template string, err error) {
+	//if the input data is more than one line, assume its a template and return that data.
+	if strings.Contains(inputData, "\n") {
+		return inputData, nil
+	}
+
+	//load data from file
+	data, err := os.ReadFile(inputData)
+	//return data if found and loaded
+	if err == nil {
+		return string(data), nil
+	}
+
+	//check for non "not found" errors
+	if !os.IsNotExist(err) {
+		return "", fmt.Errorf("failed to open file %s: %w", inputData, err)
+	}
+
+	//attempt to get data from url
+	resp, err := http.Get(inputData)
+	if err != nil {
+		return "", fmt.Errorf("failed to execute GET request data from %s: %w", inputData, err)
+	}
+	if resp.StatusCode < 200 || resp.StatusCode >= 300 {
+		return "", fmt.Errorf("got non %d status code on GET %s", resp.StatusCode, inputData)
+	}
+	data, err = io.ReadAll(resp.Body)
+	if err != nil {
+		return "", fmt.Errorf("failed to read response body from GET %s: %w", inputData, err)
+	}
+
+	return string(data), nil
 }
 
 // LoadTemplates loads all of our template files into a text/template. The
diff --git a/pkg/codegen/configuration.go b/pkg/codegen/configuration.go
index afa4fc5..9b64bfe 100644
--- a/pkg/codegen/configuration.go
+++ b/pkg/codegen/configuration.go
@@ -23,6 +23,7 @@ type Configuration struct {
 // GenerateOptions specifies which supported output formats to generate.
 type GenerateOptions struct {
 	ChiServer     bool `yaml:"chi-server,omitempty"`     // ChiServer specifies whether to generate chi server boilerplate
+	FiberServer   bool `yaml:"fiber-server,omitempty"`   // FiberServer specifies whether to generate fiber server boilerplate
 	EchoServer    bool `yaml:"echo-server,omitempty"`    // EchoServer specifies whether to generate echo server boilerplate
 	GinServer     bool `yaml:"gin-server,omitempty"`     // GinServer specifies whether to generate gin server boilerplate
 	GorillaServer bool `yaml:"gorilla-server,omitempty"` // GorillaServer specifies whether to generate Gorilla server boilerplate
@@ -84,9 +85,10 @@ type OutputOptions struct {
 	ExcludeTags   []string          `yaml:"exclude-tags,omitempty"`   // Exclude operations that have one of these tags. Ignored when empty.
 	UserTemplates map[string]string `yaml:"user-templates,omitempty"` // Override built-in templates from user-provided files
 
-	ExcludeSchemas     []string `yaml:"exclude-schemas,omitempty"`      // Exclude from generation schemas with given names. Ignored when empty.
-	ResponseTypeSuffix string   `yaml:"response-type-suffix,omitempty"` // The suffix used for responses types
-	ClientTypeName     string   `yaml:"client-type-name,omitempty"`     // Override the default generated client type with the value
+	ExcludeSchemas      []string `yaml:"exclude-schemas,omitempty"`      // Exclude from generation schemas with given names. Ignored when empty.
+	ResponseTypeSuffix  string   `yaml:"response-type-suffix,omitempty"` // The suffix used for responses types
+	ClientTypeName      string   `yaml:"client-type-name,omitempty"`     // Override the default generated client type with the value
+	InitialismOverrides bool     `yaml:"initialism-overrides,omitempty"` // Whether to use the initialism overrides
 }
 
 // UpdateDefaults sets reasonable default values for unset fields in Configuration
@@ -112,6 +114,9 @@ func (o Configuration) Validate() error {
 	if o.Generate.ChiServer {
 		nServers++
 	}
+	if o.Generate.FiberServer {
+		nServers++
+	}
 	if o.Generate.EchoServer {
 		nServers++
 	}
diff --git a/pkg/codegen/operations.go b/pkg/codegen/operations.go
index 28b991f..55cc1ba 100644
--- a/pkg/codegen/operations.go
+++ b/pkg/codegen/operations.go
@@ -293,8 +293,18 @@ func (o *OperationDefinition) GetResponseTypeDefinitions() ([]ResponseTypeDefini
 
 					var typeName string
 					switch {
-					case StringInArray(contentTypeName, contentTypesJSON):
+
+					// HAL+JSON:
+					case StringInArray(contentTypeName, contentTypesHalJSON):
+						typeName = fmt.Sprintf("HALJSON%s", ToCamelCase(responseName))
+					case "application/json" == contentTypeName:
+						// if it's the standard application/json
 						typeName = fmt.Sprintf("JSON%s", ToCamelCase(responseName))
+					// Vendored JSON
+					case StringInArray(contentTypeName, contentTypesJSON) || util.IsMediaTypeJson(contentTypeName):
+						baseTypeName := fmt.Sprintf("%s%s", ToCamelCase(contentTypeName), ToCamelCase(responseName))
+
+						typeName = strings.ReplaceAll(baseTypeName, "Json", "JSON")
 					// YAML:
 					case StringInArray(contentTypeName, contentTypesYAML):
 						typeName = fmt.Sprintf("YAML%s", ToCamelCase(responseName))
@@ -483,9 +493,16 @@ func FilterParameterDefinitionByType(params []ParameterDefinition, in string) []
 }
 
 // OperationDefinitions returns all operations for a swagger definition.
-func OperationDefinitions(swagger *openapi3.T) ([]OperationDefinition, error) {
+func OperationDefinitions(swagger *openapi3.T, initialismOverrides bool) ([]OperationDefinition, error) {
 	var operations []OperationDefinition
 
+	var toCamelCaseFunc func(string) string
+	if initialismOverrides {
+		toCamelCaseFunc = ToCamelCaseWithInitialism
+	} else {
+		toCamelCaseFunc = ToCamelCase
+	}
+
 	for _, requestPath := range SortedPathsKeys(swagger.Paths) {
 		pathItem := swagger.Paths[requestPath]
 		// These are parameters defined for all methods on a given path. They
@@ -505,13 +522,13 @@ func OperationDefinitions(swagger *openapi3.T) ([]OperationDefinition, error) {
 			}
 			// We rely on OperationID to generate function names, it's required
 			if op.OperationID == "" {
-				op.OperationID, err = generateDefaultOperationID(opName, requestPath)
+				op.OperationID, err = generateDefaultOperationID(opName, requestPath, toCamelCaseFunc)
 				if err != nil {
 					return nil, fmt.Errorf("error generating default OperationID for %s/%s: %s",
 						opName, requestPath, err)
 				}
 			} else {
-				op.OperationID = ToCamelCase(op.OperationID)
+				op.OperationID = toCamelCaseFunc(op.OperationID)
 			}
 			op.OperationID = typeNamePrefix(op.OperationID) + op.OperationID
 
@@ -550,7 +567,7 @@ func OperationDefinitions(swagger *openapi3.T) ([]OperationDefinition, error) {
 				HeaderParams: FilterParameterDefinitionByType(allParams, "header"),
 				QueryParams:  FilterParameterDefinitionByType(allParams, "query"),
 				CookieParams: FilterParameterDefinitionByType(allParams, "cookie"),
-				OperationId:  ToCamelCase(op.OperationID),
+				OperationId:  toCamelCaseFunc(op.OperationID),
 				// Replace newlines in summary.
 				Summary:         op.Summary,
 				Method:          opName,
@@ -588,7 +605,7 @@ func OperationDefinitions(swagger *openapi3.T) ([]OperationDefinition, error) {
 	return operations, nil
 }
 
-func generateDefaultOperationID(opName string, requestPath string) (string, error) {
+func generateDefaultOperationID(opName string, requestPath string, toCamelCaseFunc func(string) string) (string, error) {
 	var operationId = strings.ToLower(opName)
 
 	if opName == "" {
@@ -605,7 +622,7 @@ func generateDefaultOperationID(opName string, requestPath string) (string, erro
 		}
 	}
 
-	return ToCamelCase(operationId), nil
+	return toCamelCaseFunc(operationId), nil
 }
 
 // GenerateBodyDefinitions turns the Swagger body definitions into a list of our body
@@ -650,7 +667,7 @@ func GenerateBodyDefinitions(operationID string, bodyOrRef *openapi3.RequestBody
 		}
 
 		// If the body is a pre-defined type
-		if IsGoTypeReference(content.Schema.Ref) {
+		if content.Schema != nil && IsGoTypeReference(content.Schema.Ref) {
 			// Convert the reference path to Go type
 			refType, err := RefPathToGoType(content.Schema.Ref)
 			if err != nil {
@@ -902,6 +919,12 @@ func GenerateChiServer(t *template.Template, operations []OperationDefinition) (
 	return GenerateTemplates([]string{"chi/chi-interface.tmpl", "chi/chi-middleware.tmpl", "chi/chi-handler.tmpl"}, t, operations)
 }
 
+// GenerateFiberServer This function generates all the go code for the ServerInterface as well as
+// all the wrapper functions around our handlers.
+func GenerateFiberServer(t *template.Template, operations []OperationDefinition) (string, error) {
+	return GenerateTemplates([]string{"fiber/fiber-interface.tmpl", "fiber/fiber-middleware.tmpl", "fiber/fiber-handler.tmpl"}, t, operations)
+}
+
 // GenerateEchoServer This function generates all the go code for the ServerInterface as well as
 // all the wrapper functions around our handlers.
 func GenerateEchoServer(t *template.Template, operations []OperationDefinition) (string, error) {
@@ -921,16 +944,22 @@ func GenerateGorillaServer(t *template.Template, operations []OperationDefinitio
 }
 
 func GenerateStrictServer(t *template.Template, operations []OperationDefinition, opts Configuration) (string, error) {
-	templates := []string{"strict/strict-interface.tmpl"}
+
+	var templates []string
+
 	if opts.Generate.ChiServer || opts.Generate.GorillaServer {
-		templates = append(templates, "strict/strict-http.tmpl")
+		templates = append(templates, "strict/strict-interface.tmpl", "strict/strict-http.tmpl")
 	}
 	if opts.Generate.EchoServer {
-		templates = append(templates, "strict/strict-echo.tmpl")
+		templates = append(templates, "strict/strict-interface.tmpl", "strict/strict-echo.tmpl")
 	}
 	if opts.Generate.GinServer {
-		templates = append(templates, "strict/strict-gin.tmpl")
+		templates = append(templates, "strict/strict-interface.tmpl", "strict/strict-gin.tmpl")
+	}
+	if opts.Generate.FiberServer {
+		templates = append(templates, "strict/strict-fiber-interface.tmpl", "strict/strict-fiber.tmpl")
 	}
+
 	return GenerateTemplates(templates, t, operations)
 }
 
diff --git a/pkg/codegen/schema.go b/pkg/codegen/schema.go
index 457e102..761679d 100644
--- a/pkg/codegen/schema.go
+++ b/pkg/codegen/schema.go
@@ -241,6 +241,7 @@ func GenerateGoSchema(sref *openapi3.SchemaRef, path []string) (Schema, error) {
 			GoType:         refType,
 			Description:    schema.Description,
 			DefineViaAlias: true,
+			OAPISchema:     schema,
 		}, nil
 	}
 
@@ -262,7 +263,8 @@ func GenerateGoSchema(sref *openapi3.SchemaRef, path []string) (Schema, error) {
 		return mergedSchema, nil
 	}
 
-	// Check for custom Go type extension
+	// Check x-go-type, which will completely override the definition of this
+	// schema with the provided type.
 	if extension, ok := schema.Extensions[extPropGoType]; ok {
 		typeName, err := extTypeName(extension)
 		if err != nil {
@@ -270,6 +272,7 @@ func GenerateGoSchema(sref *openapi3.SchemaRef, path []string) (Schema, error) {
 		}
 		outSchema.GoType = typeName
 		outSchema.DefineViaAlias = true
+
 		return outSchema, nil
 	}
 
@@ -407,6 +410,28 @@ func GenerateGoSchema(sref *openapi3.SchemaRef, path []string) (Schema, error) {
 
 			outSchema.GoType = GenStructFromSchema(outSchema)
 		}
+
+		// Check for x-go-type-name. It behaves much like x-go-type, however, it will
+		// create a type definition for the named type, and use the named type in place
+		// of this schema.
+		if extension, ok := schema.Extensions[extGoTypeName]; ok {
+			typeName, err := extTypeName(extension)
+			if err != nil {
+				return outSchema, fmt.Errorf("invalid value for %q: %w", extGoTypeName, err)
+			}
+
+			newTypeDef := TypeDefinition{
+				TypeName: typeName,
+				Schema:   outSchema,
+			}
+			outSchema = Schema{
+				Description:     newTypeDef.Schema.Description,
+				GoType:          typeName,
+				DefineViaAlias:  true,
+				AdditionalTypes: []TypeDefinition{newTypeDef},
+			}
+		}
+
 		return outSchema, nil
 	} else if len(schema.Enum) > 0 {
 		err := oapiSchemaToGoType(schema, path, &outSchema)
@@ -638,17 +663,20 @@ func GenFieldsFromProperties(props []Property) []string {
 
 		field += fmt.Sprintf("    %s %s", goFieldName, p.GoTypeDef())
 
+		omitEmpty := !p.Nullable &&
+			(!p.Required || p.ReadOnly || p.WriteOnly) &&
+			(!p.Required || !p.ReadOnly || !globalState.options.Compatibility.DisableRequiredReadOnlyAsPointer)
+
 		// Support x-omitempty
-		overrideOmitEmpty := true
-		if _, ok := p.Extensions[extPropOmitEmpty]; ok {
-			if extOmitEmpty, err := extParseOmitEmpty(p.Extensions[extPropOmitEmpty]); err == nil {
-				overrideOmitEmpty = extOmitEmpty
+		if extOmitEmptyValue, ok := p.Extensions[extPropOmitEmpty]; ok {
+			if extOmitEmpty, err := extParseOmitEmpty(extOmitEmptyValue); err == nil {
+				omitEmpty = extOmitEmpty
 			}
 		}
 
 		fieldTags := make(map[string]string)
 
-		if (p.Required && !p.ReadOnly && !p.WriteOnly) || p.Nullable || !overrideOmitEmpty || (p.Required && p.ReadOnly && globalState.options.Compatibility.DisableRequiredReadOnlyAsPointer) {
+		if !omitEmpty {
 			fieldTags["json"] = p.JsonFieldName
 			if p.NeedsFormTag {
 				fieldTags["form"] = p.JsonFieldName
@@ -693,6 +721,9 @@ func additionalPropertiesType(schema Schema) string {
 	if schema.AdditionalPropertiesType.RefType != "" {
 		addPropsType = schema.AdditionalPropertiesType.RefType
 	}
+	if schema.AdditionalPropertiesType.OAPISchema != nil && schema.AdditionalPropertiesType.OAPISchema.Nullable {
+		addPropsType = "*" + addPropsType
+	}
 	return addPropsType
 }
 
diff --git a/pkg/codegen/template_helpers.go b/pkg/codegen/template_helpers.go
index 16671fb..d0c440f 100644
--- a/pkg/codegen/template_helpers.go
+++ b/pkg/codegen/template_helpers.go
@@ -34,9 +34,10 @@ const (
 )
 
 var (
-	contentTypesJSON = []string{echo.MIMEApplicationJSON, "text/x-json", "application/problem+json"}
-	contentTypesYAML = []string{"application/yaml", "application/x-yaml", "text/yaml", "text/x-yaml"}
-	contentTypesXML  = []string{echo.MIMEApplicationXML, echo.MIMETextXML, "application/problems+xml"}
+	contentTypesJSON    = []string{echo.MIMEApplicationJSON, "text/x-json", "application/problem+json"}
+	contentTypesHalJSON = []string{"application/hal+json"}
+	contentTypesYAML    = []string{"application/yaml", "application/x-yaml", "text/yaml", "text/x-yaml"}
+	contentTypesXML     = []string{echo.MIMEApplicationXML, echo.MIMETextXML, "application/problems+xml"}
 
 	responseTypeSuffix = "Response"
 
@@ -283,6 +284,7 @@ var TemplateFunctions = template.FuncMap{
 	"genParamNames":              genParamNames,
 	"genParamFmtString":          ReplacePathParamsWithStr,
 	"swaggerUriToEchoUri":        SwaggerUriToEchoUri,
+	"swaggerUriToFiberUri":       SwaggerUriToFiberUri,
 	"swaggerUriToChiUri":         SwaggerUriToChiUri,
 	"swaggerUriToGinUri":         SwaggerUriToGinUri,
 	"swaggerUriToGorillaUri":     SwaggerUriToGorillaUri,
diff --git a/pkg/codegen/templates/client.tmpl b/pkg/codegen/templates/client.tmpl
index 58df443..f3afe84 100644
--- a/pkg/codegen/templates/client.tmpl
+++ b/pkg/codegen/templates/client.tmpl
@@ -194,36 +194,38 @@ func New{{$opid}}Request{{if .HasBody}}WithBody{{end}}(server string{{genParamAr
     }
 
 {{if .QueryParams}}
-    queryValues := queryURL.Query()
-{{range $paramIdx, $param := .QueryParams}}
-    {{if not .Required}} if params.{{.GoName}} != nil { {{end}}
-    {{if .IsPassThrough}}
-    queryValues.Add("{{.ParamName}}", {{if not .Required}}*{{end}}params.{{.GoName}})
-    {{end}}
-    {{if .IsJson}}
-    if queryParamBuf, err := json.Marshal({{if not .Required}}*{{end}}params.{{.GoName}}); err != nil {
-        return nil, err
-    } else {
-        queryValues.Add("{{.ParamName}}", string(queryParamBuf))
-    }
+    if params != nil {
+        queryValues := queryURL.Query()
+            {{range $paramIdx, $param := .QueryParams}}
+            {{if not .Required}} if params.{{.GoName}} != nil { {{end}}
+            {{if .IsPassThrough}}
+            queryValues.Add("{{.ParamName}}", {{if not .Required}}*{{end}}params.{{.GoName}})
+            {{end}}
+            {{if .IsJson}}
+            if queryParamBuf, err := json.Marshal({{if not .Required}}*{{end}}params.{{.GoName}}); err != nil {
+                return nil, err
+            } else {
+                queryValues.Add("{{.ParamName}}", string(queryParamBuf))
+            }
 
-    {{end}}
-    {{if .IsStyled}}
-    if queryFrag, err := runtime.StyleParamWithLocation("{{.Style}}", {{.Explode}}, "{{.ParamName}}", runtime.ParamLocationQuery, {{if not .Required}}*{{end}}params.{{.GoName}}); err != nil {
-        return nil, err
-    } else if parsed, err := url.ParseQuery(queryFrag); err != nil {
-       return nil, err
-    } else {
-       for k, v := range parsed {
-           for _, v2 := range v {
-               queryValues.Add(k, v2)
-           }
-       }
+            {{end}}
+            {{if .IsStyled}}
+            if queryFrag, err := runtime.StyleParamWithLocation("{{.Style}}", {{.Explode}}, "{{.ParamName}}", runtime.ParamLocationQuery, {{if not .Required}}*{{end}}params.{{.GoName}}); err != nil {
+                return nil, err
+            } else if parsed, err := url.ParseQuery(queryFrag); err != nil {
+               return nil, err
+            } else {
+               for k, v := range parsed {
+                   for _, v2 := range v {
+                       queryValues.Add(k, v2)
+                   }
+               }
+            }
+            {{end}}
+            {{if not .Required}}}{{end}}
+        {{end}}
+        queryURL.RawQuery = queryValues.Encode()
     }
-    {{end}}
-    {{if not .Required}}}{{end}}
-{{end}}
-    queryURL.RawQuery = queryValues.Encode()
 {{end}}{{/* if .QueryParams */}}
     req, err := http.NewRequest("{{.Method}}", queryURL.String(), {{if .HasBody}}body{{else}}nil{{end}})
     if err != nil {
diff --git a/pkg/codegen/templates/fiber/fiber-handler.tmpl b/pkg/codegen/templates/fiber/fiber-handler.tmpl
new file mode 100644
index 0000000..4a55b3c
--- /dev/null
+++ b/pkg/codegen/templates/fiber/fiber-handler.tmpl
@@ -0,0 +1,25 @@
+// FiberServerOptions provides options for the Fiber server.
+type FiberServerOptions struct {
+    BaseURL string
+    Middlewares []MiddlewareFunc
+}
+
+// RegisterHandlers creates http.Handler with routing matching OpenAPI spec.
+func RegisterHandlers(router fiber.Router, si ServerInterface) {
+  RegisterHandlersWithOptions(router, si, FiberServerOptions{})
+}
+
+// RegisterHandlersWithOptions creates http.Handler with additional options
+func RegisterHandlersWithOptions(router fiber.Router, si ServerInterface, options FiberServerOptions) {
+{{if .}}wrapper := ServerInterfaceWrapper{
+Handler: si,
+}
+
+for _, m := range options.Middlewares {
+    router.Use(m)
+}
+{{end}}
+{{range .}}
+router.{{.Method | lower | title }}(options.BaseURL+"{{.Path | swaggerUriToFiberUri}}", wrapper.{{.OperationId}})
+{{end}}
+}
diff --git a/pkg/codegen/templates/fiber/fiber-interface.tmpl b/pkg/codegen/templates/fiber/fiber-interface.tmpl
new file mode 100644
index 0000000..8ef90a8
--- /dev/null
+++ b/pkg/codegen/templates/fiber/fiber-interface.tmpl
@@ -0,0 +1,7 @@
+// ServerInterface represents all server handlers.
+type ServerInterface interface {
+{{range .}}{{.SummaryAsComment }}
+// ({{.Method}} {{.Path}})
+{{.OperationId}}(c *fiber.Ctx{{genParamArgs .PathParams}}{{if .RequiresParamObject}}, params {{.OperationId}}Params{{end}}) error
+{{end}}
+}
diff --git a/pkg/codegen/templates/fiber/fiber-middleware.tmpl b/pkg/codegen/templates/fiber/fiber-middleware.tmpl
new file mode 100644
index 0000000..fd8d00d
--- /dev/null
+++ b/pkg/codegen/templates/fiber/fiber-middleware.tmpl
@@ -0,0 +1,169 @@
+// ServerInterfaceWrapper converts contexts to parameters.
+type ServerInterfaceWrapper struct {
+    Handler ServerInterface
+}
+
+type MiddlewareFunc fiber.Handler
+
+{{range .}}{{$opid := .OperationId}}
+
+// {{$opid}} operation middleware
+func (siw *ServerInterfaceWrapper) {{$opid}}(c *fiber.Ctx) error {
+
+  {{if or .RequiresParamObject (gt (len .PathParams) 0) }}
+  var err error
+  {{end}}
+
+  {{range .PathParams}}// ------------- Path parameter "{{.ParamName}}" -------------
+  var {{$varName := .GoVariableName}}{{$varName}} {{.TypeDef}}
+
+  {{if .IsPassThrough}}
+  {{$varName}} = c.Query("{{.ParamName}}")
+  {{end}}
+  {{if .IsJson}}
+  err = json.Unmarshal([]byte(c.Query("{{.ParamName}}")), &{{$varName}})
+  if err != nil {
+    return fiber.NewError(fiber.StatusBadRequest, fmt.Errorf("Error unmarshalling parameter '{{.ParamName}}' as JSON: %w", err).Error())
+  }
+  {{end}}
+  {{if .IsStyled}}
+  err = runtime.BindStyledParameter("{{.Style}}",{{.Explode}}, "{{.ParamName}}", c.Params("{{.ParamName}}"), &{{$varName}})
+  if err != nil {
+    return fiber.NewError(fiber.StatusBadRequest, fmt.Errorf("Invalid format for parameter {{.ParamName}}: %w", err).Error())
+  }
+  {{end}}
+
+  {{end}}
+
+{{range .SecurityDefinitions}}
+  c.Context().SetUserValue({{.ProviderName | ucFirst}}Scopes, {{toStringArray .Scopes}})
+{{end}}
+
+  {{if .RequiresParamObject}}
+    // Parameter object where we will unmarshal all parameters from the context
+    var params {{.OperationId}}Params
+
+    {{if .QueryParams}}
+    var query url.Values
+    query, err = url.ParseQuery(string(c.Request().URI().QueryString()))
+    if err != nil {
+      return fiber.NewError(fiber.StatusBadRequest, fmt.Errorf("Invalid format for query string: %w", err).Error())
+    }
+    {{end}}
+
+    {{range $paramIdx, $param := .QueryParams}}
+      {{- if (or (or .Required .IsPassThrough) (or .IsJson .IsStyled)) -}}
+        // ------------- {{if .Required}}Required{{else}}Optional{{end}} query parameter "{{.ParamName}}" -------------
+      {{ end }}
+      {{ if (or (or .Required .IsPassThrough) .IsJson) }}
+        if paramValue := c.Query("{{.ParamName}}"); paramValue != "" {
+
+        {{if .IsPassThrough}}
+          params.{{.GoName}} = {{if not .Required}}&{{end}}paramValue
+        {{end}}
+
+        {{if .IsJson}}
+          var value {{.TypeDef}}
+          err = json.Unmarshal([]byte(paramValue), &value)
+          if err != nil {
+            return fiber.NewError(fiber.StatusBadRequest, fmt.Errorf("Error unmarshalling parameter '{{.ParamName}}' as JSON: %w", err).Error())
+          }
+
+          params.{{.GoName}} = {{if not .Required}}&{{end}}value
+        {{end}}
+        }{{if .Required}} else {
+            err = fmt.Errorf("Query argument {{.ParamName}} is required, but not found")
+            c.Status(fiber.StatusBadRequest).JSON(err)
+            return err
+        }{{end}}
+      {{end}}
+      {{if .IsStyled}}
+      err = runtime.BindQueryParameter("{{.Style}}", {{.Explode}}, {{.Required}}, "{{.ParamName}}", query, &params.{{.GoName}})
+      if err != nil {
+        return fiber.NewError(fiber.StatusBadRequest, fmt.Errorf("Invalid format for parameter {{.ParamName}}: %w", err).Error())
+      }
+      {{end}}
+  {{end}}
+
+    {{if .HeaderParams}}
+      headers := c.GetReqHeaders()
+
+      {{range .HeaderParams}}// ------------- {{if .Required}}Required{{else}}Optional{{end}} header parameter "{{.ParamName}}" -------------
+        if value, found := headers[http.CanonicalHeaderKey("{{.ParamName}}")]; found {
+          var {{.GoName}} {{.TypeDef}}
+
+        {{if .IsPassThrough}}
+          params.{{.GoName}} = {{if not .Required}}&{{end}}value
+        {{end}}
+
+        {{if .IsJson}}
+          err = json.Unmarshal([]byte(value), &{{.GoName}})
+          if err != nil {
+            return fiber.NewError(fiber.StatusBadRequest, fmt.Errorf("Error unmarshalling parameter '{{.ParamName}}' as JSON: %w", err).Error())
+          }
+        {{end}}
+
+        {{if .IsStyled}}
+          err = runtime.BindStyledParameterWithLocation("{{.Style}}",{{.Explode}}, "{{.ParamName}}", runtime.ParamLocationHeader, value, &{{.GoName}})
+          if err != nil {
+            return fiber.NewError(fiber.StatusBadRequest, fmt.Errorf("Invalid format for parameter {{.ParamName}}: %w", err).Error())
+          }
+        {{end}}
+
+          params.{{.GoName}} = {{if not .Required}}&{{end}}{{.GoName}}
+
+        } {{if .Required}}else {
+            err = fmt.Errorf("Header parameter {{.ParamName}} is required, but not found: %w", err)
+            return fiber.NewError(fiber.StatusBadRequest, err.Error())
+        }{{end}}
+
+      {{end}}
+    {{end}}
+
+    {{range .CookieParams}}
+      var cookie string
+
+      if cookie = c.Cookies("{{.ParamName}}"); cookie == "" {
+
+      {{- if .IsPassThrough}}
+        params.{{.GoName}} = {{if not .Required}}&{{end}}cookie
+      {{end}}
+
+      {{- if .IsJson}}
+        var value {{.TypeDef}}
+        var decoded string
+        decoded, err := url.QueryUnescape(cookie)
+        if err != nil {
+          return fiber.NewError(fiber.StatusBadRequest, fmt.Errorf("Error unescaping cookie parameter '{{.ParamName}}': %w", err).Error())
+        }
+
+        err = json.Unmarshal([]byte(decoded), &value)
+        if err != nil {
+          return fiber.NewError(fiber.StatusBadRequest, fmt.Errorf("Error unmarshalling parameter '{{.ParamName}}' as JSON: %w", err).Error())
+        }
+
+        params.{{.GoName}} = {{if not .Required}}&{{end}}value
+      {{end}}
+
+      {{- if .IsStyled}}
+        var value {{.TypeDef}}
+        err = runtime.BindStyledParameter("simple",{{.Explode}}, "{{.ParamName}}", cookie, &value)
+        if err != nil {
+          return fiber.NewError(fiber.StatusBadRequest, fmt.Errorf("Invalid format for parameter {{.ParamName}}: %w", err).Error())
+        }
+        params.{{.GoName}} = {{if not .Required}}&{{end}}value
+      {{end}}
+
+      }
+
+      {{- if .Required}} else {
+        err = fmt.Errorf("Query argument {{.ParamName}} is required, but not found")
+        return fiber.NewError(fiber.StatusBadRequest, err.Error())
+      }
+      {{- end}}
+    {{end}}
+  {{end}}
+
+  return siw.Handler.{{.OperationId}}(c{{genParamNames .PathParams}}{{if .RequiresParamObject}}, params{{end}})
+}
+{{end}}
diff --git a/pkg/codegen/templates/gin/gin-wrappers.tmpl b/pkg/codegen/templates/gin/gin-wrappers.tmpl
index c34a522..e30e839 100644
--- a/pkg/codegen/templates/gin/gin-wrappers.tmpl
+++ b/pkg/codegen/templates/gin/gin-wrappers.tmpl
@@ -69,7 +69,7 @@ func (siw *ServerInterfaceWrapper) {{$opid}}(c *gin.Context) {
           params.{{.GoName}} = {{if not .Required}}&{{end}}value
         {{end}}
         }{{if .Required}} else {
-           siw.ErrorHandler(c, fmt.Errorf("Query argument {{.ParamName}} is required, but not found: %s", err), http.StatusBadRequest)
+           siw.ErrorHandler(c, fmt.Errorf("Query argument {{.ParamName}} is required, but not found"), http.StatusBadRequest)
            return
         }{{end}}
       {{end}}
@@ -118,7 +118,7 @@ func (siw *ServerInterfaceWrapper) {{$opid}}(c *gin.Context) {
           params.{{.GoName}} = {{if not .Required}}&{{end}}{{.GoName}}
 
         } {{if .Required}}else {
-            siw.ErrorHandler(c, fmt.Errorf("Header parameter {{.ParamName}} is required, but not found: %s", err), http.StatusBadRequest)
+            siw.ErrorHandler(c, fmt.Errorf("Header parameter {{.ParamName}} is required, but not found"), http.StatusBadRequest)
             return
         }{{end}}
 
diff --git a/pkg/codegen/templates/imports.tmpl b/pkg/codegen/templates/imports.tmpl
index ef43ef3..f69a492 100644
--- a/pkg/codegen/templates/imports.tmpl
+++ b/pkg/codegen/templates/imports.tmpl
@@ -27,6 +27,7 @@ import (
 	"github.com/go-chi/chi/v5"
 	"github.com/labstack/echo/v4"
 	"github.com/gin-gonic/gin"
+	"github.com/gofiber/fiber/v2"
 	"github.com/gorilla/mux"
 	{{- range .ExternalImports}}
 	{{ . }}
diff --git a/pkg/codegen/templates/strict/strict-echo.tmpl b/pkg/codegen/templates/strict/strict-echo.tmpl
index 4bd6aec..86def37 100644
--- a/pkg/codegen/templates/strict/strict-echo.tmpl
+++ b/pkg/codegen/templates/strict/strict-echo.tmpl
@@ -1,6 +1,5 @@
-type StrictHandlerFunc func(ctx echo.Context, args interface{}) (interface{}, error)
-
-type StrictMiddlewareFunc func(f StrictHandlerFunc, operationID string) StrictHandlerFunc
+type StrictHandlerFunc = runtime.StrictEchoHandlerFunc
+type StrictMiddlewareFunc = runtime.StrictEchoMiddlewareFunc
 
 func NewStrictHandler(ssi StrictServerInterface, middlewares []StrictMiddlewareFunc) ServerInterface {
     return &strictHandler{ssi: ssi, middlewares: middlewares}
diff --git a/pkg/codegen/templates/strict/strict-fiber-interface.tmpl b/pkg/codegen/templates/strict/strict-fiber-interface.tmpl
new file mode 100644
index 0000000..6fdef09
--- /dev/null
+++ b/pkg/codegen/templates/strict/strict-fiber-interface.tmpl
@@ -0,0 +1,137 @@
+{{range .}}
+    {{$opid := .OperationId -}}
+    type {{$opid | ucFirst}}RequestObject struct {
+        {{range .PathParams -}}
+            {{.GoName | ucFirst}} {{.TypeDef}} {{.JsonTag}}
+        {{end -}}
+        {{if .RequiresParamObject -}}
+            Params {{$opid}}Params
+        {{end -}}
+        {{if .HasMaskedRequestContentTypes -}}
+            ContentType string
+        {{end -}}
+        {{$multipleBodies := gt (len .Bodies) 1 -}}
+        {{range .Bodies -}}
+            {{if $multipleBodies}}{{.NameTag}}{{end}}Body {{if eq .NameTag "Multipart"}}*multipart.Reader{{else if ne .NameTag ""}}*{{$opid}}{{.NameTag}}RequestBody{{else}}io.Reader{{end}}
+        {{end -}}
+    }
+
+    type {{$opid | ucFirst}}ResponseObject interface {
+        Visit{{$opid}}Response(ctx *fiber.Ctx) error
+    }
+
+    {{range .Responses}}
+        {{$statusCode := .StatusCode -}}
+        {{$hasHeaders := ne 0 (len .Headers) -}}
+        {{$fixedStatusCode := .HasFixedStatusCode -}}
+        {{$isRef := .IsRef -}}
+        {{$ref := .Ref  | ucFirst -}}
+        {{$headers := .Headers -}}
+
+        {{if (and $hasHeaders (not $isRef)) -}}
+            type {{$opid}}{{$statusCode}}ResponseHeaders struct {
+                {{range .Headers -}}
+                    {{.GoName}} {{.Schema.TypeDecl}}
+                {{end -}}
+            }
+        {{end}}
+
+        {{range .Contents}}
+            {{$receiverTypeName := printf "%s%s%s%s" $opid $statusCode .NameTagOrContentType "Response"}}
+            {{if and $fixedStatusCode $isRef -}}
+                type {{$receiverTypeName}} struct{ {{$ref}}{{.NameTagOrContentType}}Response }
+            {{else if and (not $hasHeaders) ($fixedStatusCode) (.IsSupported) -}}
+                type {{$receiverTypeName}} {{if eq .NameTag "Multipart"}}func(writer *multipart.Writer)error{{else if .IsSupported}}{{if .Schema.IsRef}}={{end}} {{.Schema.TypeDecl}}{{else}}io.Reader{{end}}
+            {{else -}}
+                type {{$receiverTypeName}} struct {
+                    Body {{if eq .NameTag "Multipart"}}func(writer *multipart.Writer)error{{else if .IsSupported}}{{.Schema.TypeDecl}}{{else}}io.Reader{{end}}
+                    {{if $hasHeaders -}}
+                        Headers {{if $isRef}}{{$ref}}{{else}}{{$opid}}{{$statusCode}}{{end}}ResponseHeaders
+                    {{end -}}
+
+                    {{if not $fixedStatusCode -}}
+                        StatusCode int
+                    {{end -}}
+
+                    {{if not .HasFixedContentType -}}
+                        ContentType string
+                    {{end -}}
+
+                    {{if not .IsSupported -}}
+                        ContentLength int64
+                    {{end -}}
+                }
+            {{end}}
+
+            func (response {{$receiverTypeName}}) Visit{{$opid}}Response(ctx *fiber.Ctx) error {
+                {{range $headers -}}
+                    ctx.Response().Header.Set("{{.Name}}", fmt.Sprint(response.Headers.{{.GoName}}))
+                {{end -}}
+                {{if eq .NameTag "Multipart" -}}
+                    writer := multipart.NewWriter(ctx.Response().BodyWriter())
+                {{end -}}
+                ctx.Response().Header.Set("Content-Type", {{if eq .NameTag "Multipart"}}writer.FormDataContentType(){{else if .HasFixedContentType }}"{{.ContentType}}"{{else}}response.ContentType{{end}})
+                {{if not .IsSupported -}}
+                    if response.ContentLength != 0 {
+                        ctx.Response().Header.Set("Content-Length", fmt.Sprint(response.ContentLength))
+                    }
+                {{end -}}
+                ctx.Status({{if $fixedStatusCode}}{{$statusCode}}{{else}}response.StatusCode{{end}})
+                {{$hasBodyVar := or ($hasHeaders) (not $fixedStatusCode) (not .IsSupported)}}
+                {{if eq .NameTag "JSON" -}}
+                    return ctx.JSON(&{{if $hasBodyVar}}response.Body{{else}}response{{end}})
+                {{else if eq .NameTag "Text" -}}
+                    _, err := ctx.WriteString(string({{if $hasBodyVar}}response.Body{{else}}response{{end}}))
+                    return err
+                {{else if eq .NameTag "Formdata" -}}
+                    if form, err := runtime.MarshalForm({{if $hasBodyVar}}response.Body{{else}}response{{end}}, nil); err != nil {
+                        return err
+                    } else {
+                        _, err := ctx.WriteString(form.Encode())
+                        return err
+                    }
+                {{else if eq .NameTag "Multipart" -}}
+                    defer writer.Close()
+                    return {{if $hasBodyVar}}response.Body{{else}}response{{end}}(writer);
+                {{else -}}
+                    if closer, ok := response.Body.(io.ReadCloser); ok {
+                        defer closer.Close()
+                    }
+                    _, err := io.Copy(ctx.Response().BodyWriter(), response.Body)
+                    return err
+                {{end}}{{/* if eq .NameTag "JSON" */ -}}
+            }
+        {{end}}
+
+        {{if eq 0 (len .Contents) -}}
+            {{if and $fixedStatusCode $isRef -}}
+                type {{$opid}}{{$statusCode}}Response = {{$ref}}Response
+            {{else -}}
+                type {{$opid}}{{$statusCode}}Response struct {
+                    {{if $hasHeaders -}}
+                        Headers {{if $isRef}}{{$ref}}{{else}}{{$opid}}{{$statusCode}}{{end}}ResponseHeaders
+                    {{end}}
+                    {{if not $fixedStatusCode -}}
+                        StatusCode int
+                    {{end -}}
+                }
+            {{end -}}
+            func (response {{$opid}}{{$statusCode}}Response) Visit{{$opid}}Response(ctx *fiber.Ctx) error {
+                {{range $headers -}}
+                    ctx.Response().Header.Set("{{.Name}}", fmt.Sprint(response.Headers.{{.GoName}}))
+                {{end -}}
+                ctx.Status({{if $fixedStatusCode}}{{$statusCode}}{{else}}response.StatusCode{{end}})
+                return nil
+            }
+        {{end}}
+    {{end}}
+{{end}}
+
+// StrictServerInterface represents all server handlers.
+type StrictServerInterface interface {
+{{range .}}{{.SummaryAsComment }}
+// ({{.Method}} {{.Path}})
+{{$opid := .OperationId -}}
+{{$opid}}(ctx context.Context, request {{$opid | ucFirst}}RequestObject) ({{$opid | ucFirst}}ResponseObject, error)
+{{end}}{{/* range . */ -}}
+}
diff --git a/pkg/codegen/templates/strict/strict-fiber.tmpl b/pkg/codegen/templates/strict/strict-fiber.tmpl
new file mode 100644
index 0000000..38a819f
--- /dev/null
+++ b/pkg/codegen/templates/strict/strict-fiber.tmpl
@@ -0,0 +1,80 @@
+type StrictHandlerFunc func(ctx *fiber.Ctx, args interface{}) (interface{}, error)
+
+type StrictMiddlewareFunc func(f StrictHandlerFunc, operationID string) StrictHandlerFunc
+
+func NewStrictHandler(ssi StrictServerInterface, middlewares []StrictMiddlewareFunc) ServerInterface {
+    return &strictHandler{ssi: ssi, middlewares: middlewares}
+}
+
+type strictHandler struct {
+    ssi StrictServerInterface
+    middlewares []StrictMiddlewareFunc
+}
+
+{{range .}}
+    {{$opid := .OperationId}}
+    // {{$opid}} operation middleware
+    func (sh *strictHandler) {{.OperationId}}(ctx *fiber.Ctx{{genParamArgs .PathParams}}{{if .RequiresParamObject}}, params {{.OperationId}}Params{{end}}) error {
+        var request {{$opid | ucFirst}}RequestObject
+
+        {{range .PathParams -}}
+            {{$varName := .GoVariableName -}}
+            request.{{.GoName}} = {{.GoVariableName}}
+        {{end -}}
+
+        {{if .RequiresParamObject -}}
+            request.Params = params
+        {{end -}}
+
+        {{ if .HasMaskedRequestContentTypes -}}
+            request.ContentType = string(ctx.Request().Header.ContentType())
+        {{end -}}
+
+        {{$multipleBodies := gt (len .Bodies) 1 -}}
+        {{range .Bodies -}}
+            {{if $multipleBodies}}if strings.HasPrefix(string(ctx.Request().Header.ContentType()), "{{.ContentType}}") { {{end}}
+                {{if eq .NameTag "JSON" -}}
+                    var body {{$opid}}{{.NameTag}}RequestBody
+                    if err := ctx.BodyParser(&body); err != nil {
+                        return fiber.NewError(fiber.StatusBadRequest, err.Error())
+                    }
+                    request.{{if $multipleBodies}}{{.NameTag}}{{end}}Body = &body
+                {{else if eq .NameTag "Formdata" -}}
+                    var body {{$opid}}{{.NameTag}}RequestBody
+                    if err := ctx.BodyParser(&body); err != nil {
+                        return fiber.NewError(fiber.StatusBadRequest, err.Error())
+                    }
+                    request.{{if $multipleBodies}}{{.NameTag}}{{end}}Body = &body
+                {{else if eq .NameTag "Multipart" -}}
+                    request.{{if $multipleBodies}}{{.NameTag}}{{end}}Body = multipart.NewReader(bytes.NewReader(ctx.Request().Body()), string(ctx.Request().Header.MultipartFormBoundary()))
+                {{else if eq .NameTag "Text" -}}
+                    data := ctx.Request().Body()
+                    body := {{$opid}}{{.NameTag}}RequestBody(data)
+                    request.{{if $multipleBodies}}{{.NameTag}}{{end}}Body = &body
+                {{else -}}
+                    request.{{if $multipleBodies}}{{.NameTag}}{{end}}Body = bytes.NewReader(ctx.Request().Body())
+                {{end}}{{/* if eq .NameTag "JSON" */ -}}
+            {{if $multipleBodies}}}{{end}}
+        {{end}}{{/* range .Bodies */}}
+
+        handler := func(ctx *fiber.Ctx, request interface{}) (interface{}, error) {
+            return sh.ssi.{{.OperationId}}(ctx.UserContext(), request.({{$opid | ucFirst}}RequestObject))
+        }
+        for _, middleware := range sh.middlewares {
+            handler = middleware(handler, "{{.OperationId}}")
+        }
+
+        response, err := handler(ctx, request)
+
+        if err != nil {
+            return fiber.NewError(fiber.StatusBadRequest, err.Error())
+        } else if validResponse, ok := response.({{$opid | ucFirst}}ResponseObject); ok {
+            if err := validResponse.Visit{{$opid}}Response(ctx); err != nil {
+                return fiber.NewError(fiber.StatusBadRequest, err.Error())
+            }
+        } else if response != nil {
+            return fmt.Errorf("Unexpected response type: %T", response)
+        }
+        return nil
+    }
+{{end}}
diff --git a/pkg/codegen/templates/strict/strict-gin.tmpl b/pkg/codegen/templates/strict/strict-gin.tmpl
index 9622fb2..0c36f40 100644
--- a/pkg/codegen/templates/strict/strict-gin.tmpl
+++ b/pkg/codegen/templates/strict/strict-gin.tmpl
@@ -1,6 +1,5 @@
-type StrictHandlerFunc func(ctx *gin.Context, args interface{}) (interface{}, error)
-
-type StrictMiddlewareFunc func(f StrictHandlerFunc, operationID string) StrictHandlerFunc
+type StrictHandlerFunc = runtime.StrictGinHandlerFunc
+type StrictMiddlewareFunc = runtime.StrictGinMiddlewareFunc
 
 func NewStrictHandler(ssi StrictServerInterface, middlewares []StrictMiddlewareFunc) ServerInterface {
     return &strictHandler{ssi: ssi, middlewares: middlewares}
@@ -83,6 +82,7 @@ type strictHandler struct {
 
         if err != nil {
             ctx.Error(err)
+            ctx.Status(http.StatusInternalServerError)
         } else if validResponse, ok := response.({{$opid | ucFirst}}ResponseObject); ok {
             if err := validResponse.Visit{{$opid}}Response(ctx.Writer); err != nil {
                 ctx.Error(err)
diff --git a/pkg/codegen/templates/strict/strict-http.tmpl b/pkg/codegen/templates/strict/strict-http.tmpl
index d04503d..b16ed1d 100644
--- a/pkg/codegen/templates/strict/strict-http.tmpl
+++ b/pkg/codegen/templates/strict/strict-http.tmpl
@@ -1,6 +1,5 @@
-type StrictHandlerFunc func(ctx context.Context, w http.ResponseWriter, r *http.Request, args interface{}) (interface{}, error)
-
-type StrictMiddlewareFunc func(f StrictHandlerFunc, operationID string) StrictHandlerFunc
+type StrictHandlerFunc = runtime.StrictHttpHandlerFunc
+type StrictMiddlewareFunc = runtime.StrictHttpMiddlewareFunc
 
 type StrictHTTPServerOptions struct {
     RequestErrorHandlerFunc  func(w http.ResponseWriter, r *http.Request, err error)
diff --git a/pkg/codegen/utils.go b/pkg/codegen/utils.go
index c386cd3..cf8826e 100644
--- a/pkg/codegen/utils.go
+++ b/pkg/codegen/utils.go
@@ -17,6 +17,7 @@ import (
 	"fmt"
 	"go/token"
 	"net/url"
+	"reflect"
 	"regexp"
 	"sort"
 	"strconv"
@@ -160,6 +161,24 @@ func ToCamelCase(str string) string {
 	return n
 }
 
+func ToCamelCaseWithInitialism(str string) string {
+	return replaceInitialism(ToCamelCase(str))
+}
+
+func replaceInitialism(s string) string {
+	// These strings do not apply CamelCase
+	// Do not do CamelCase when these characters match when the preceding character is lowercase
+	// ["Acl", "Api", "Ascii", "Cpu", "Css", "Dns", "Eof", "Guid", "Html", "Http", "Https", "Id", "Ip", "Json", "Qps", "Ram", "Rpc", "Sla", "Smtp", "Sql", "Ssh", "Tcp", "Tls", "Ttl", "Udp", "Ui", "Gid", "Uid", "Uuid", "Uri", "Url", "Utf8", "Vm", "Xml", "Xmpp", "Xsrf", "Xss", "Sip", "Rtp", "Amqp", "Db", "Ts"]
+	targetWordRegex := regexp.MustCompile(`(?i)(Acl|Api|Ascii|Cpu|Css|Dns|Eof|Guid|Html|Http|Https|Id|Ip|Json|Qps|Ram|Rpc|Sla|Smtp|Sql|Ssh|Tcp|Tls|Ttl|Udp|Ui|Gid|Uid|Uuid|Uri|Url|Utf8|Vm|Xml|Xmpp|Xsrf|Xss|Sip|Rtp|Amqp|Db|Ts)`)
+	return targetWordRegex.ReplaceAllStringFunc(s, func(s string) string {
+		// If the preceding character is lowercase, do not do CamelCase
+		if unicode.IsLower(rune(s[0])) {
+			return s
+		}
+		return strings.ToUpper(s)
+	})
+}
+
 // SortedSchemaKeys returns the keys of the given SchemaRef dictionary in sorted
 // order, since Golang scrambles dictionary keys
 func SortedSchemaKeys(dict map[string]*openapi3.SchemaRef) []string {
@@ -350,7 +369,7 @@ func refPathToGoType(refPath string, local bool) (string, error) {
 		return "", fmt.Errorf("unsupported reference: %s", refPath)
 	}
 	remoteComponent, flatComponent := pathParts[0], pathParts[1]
-	if goImport, ok := importMapping[remoteComponent]; !ok {
+	if goImport, ok := globalState.importMapping[remoteComponent]; !ok {
 		return "", fmt.Errorf("unrecognized external reference '%s'; please provide the known import for this reference using option --import-mapping", remoteComponent)
 	} else {
 		goType, err := refPathToGoType("#"+flatComponent, false)
@@ -502,7 +521,7 @@ func IsGoKeyword(str string) bool {
 }
 
 // IsPredeclaredGoIdentifier returns whether the given string
-// is a predefined go indentifier.
+// is a predefined go identifier.
 //
 // See https://golang.org/ref/spec#Predeclared_identifiers
 func IsPredeclaredGoIdentifier(str string) bool {
@@ -709,8 +728,8 @@ func stringToGoCommentWithPrefix(in, prefix string) string {
 	}
 
 	// Normalize newlines from Windows/Mac to Linux
-	in = strings.Replace(in, "\r\n", "\n", -1)
-	in = strings.Replace(in, "\r", "\n", -1)
+	in = strings.ReplaceAll(in, "\r\n", "\n")
+	in = strings.ReplaceAll(in, "\r", "\n")
 
 	// Add comment to each line
 	var lines []string
@@ -904,5 +923,5 @@ func TypeDefinitionsEquivalent(t1, t2 TypeDefinition) bool {
 	if t1.TypeName != t2.TypeName {
 		return false
 	}
-	return t1.Schema.OAPISchema == t2.Schema.OAPISchema
+	return reflect.DeepEqual(t1.Schema.OAPISchema, t2.Schema.OAPISchema)
 }
diff --git a/pkg/fiber-middleware/oapi_validate.go b/pkg/fiber-middleware/oapi_validate.go
new file mode 100644
index 0000000..ef30dd8
--- /dev/null
+++ b/pkg/fiber-middleware/oapi_validate.go
@@ -0,0 +1,196 @@
+// Package middleware implements middleware function for server implementations,
+// which validates incoming HTTP requests to make sure that they conform to the given OAPI 3.0 specification.
+// When OAPI validation fails on the request, we return an HTTP/400.
+package middleware
+
+import (
+	"context"
+	"errors"
+	"fmt"
+	"net/http"
+	"os"
+	"strings"
+
+	"github.com/getkin/kin-openapi/openapi3"
+	"github.com/getkin/kin-openapi/openapi3filter"
+	"github.com/getkin/kin-openapi/routers"
+	"github.com/getkin/kin-openapi/routers/gorillamux"
+	"github.com/gofiber/fiber/v2"
+	"github.com/gofiber/fiber/v2/middleware/adaptor"
+)
+
+type ctxKeyFiberContext struct{}
+type ctxKeyUserData struct{}
+
+// OapiValidatorFromYamlFile creates a validator middleware from a YAML file path
+func OapiValidatorFromYamlFile(path string) (fiber.Handler, error) {
+
+	data, err := os.ReadFile(path)
+	if err != nil {
+		return nil, fmt.Errorf("error reading %s: %s", path, err)
+	}
+
+	swagger, err := openapi3.NewLoader().LoadFromData(data)
+	if err != nil {
+		return nil, fmt.Errorf("error parsing %s as Swagger YAML: %s",
+			path, err)
+	}
+
+	return OapiRequestValidator(swagger), nil
+}
+
+// OapiRequestValidator is a fiber middleware function which validates incoming HTTP requests
+// to make sure that they conform to the given OAPI 3.0 specification. When
+// OAPI validation fails on the request, we return an HTTP/400 with error message
+func OapiRequestValidator(swagger *openapi3.T) fiber.Handler {
+	return OapiRequestValidatorWithOptions(swagger, nil)
+}
+
+// ErrorHandler is called when there is an error in validation
+type ErrorHandler func(c *fiber.Ctx, message string, statusCode int)
+
+// MultiErrorHandler is called when oapi returns a MultiError type
+type MultiErrorHandler func(openapi3.MultiError) error
+
+// Options to customize request validation. These are passed through to
+// openapi3filter.
+type Options struct {
+	Options           openapi3filter.Options
+	ErrorHandler      ErrorHandler
+	ParamDecoder      openapi3filter.ContentParameterDecoder
+	UserData          interface{}
+	MultiErrorHandler MultiErrorHandler
+}
+
+// OapiRequestValidatorWithOptions creates a validator from a swagger object, with validation options
+func OapiRequestValidatorWithOptions(swagger *openapi3.T, options *Options) fiber.Handler {
+
+	router, err := gorillamux.NewRouter(swagger)
+	if err != nil {
+		panic(err)
+	}
+
+	return func(c *fiber.Ctx) error {
+
+		err := ValidateRequestFromContext(c, router, options)
+		if err != nil {
+			if options != nil && options.ErrorHandler != nil {
+				options.ErrorHandler(c, err.Error(), http.StatusBadRequest)
+				// in case the handler didn't internally call Abort, stop the chain
+				return nil
+			} else {
+				// note: I am not sure if this is the best way to handle this
+				return fiber.NewError(http.StatusBadRequest, err.Error())
+			}
+		}
+		return c.Next()
+	}
+}
+
+// ValidateRequestFromContext is called from the middleware above and actually does the work
+// of validating a request.
+func ValidateRequestFromContext(c *fiber.Ctx, router routers.Router, options *Options) error {
+
+	r, err := adaptor.ConvertRequest(c, false)
+	if err != nil {
+		return err
+	}
+
+	route, pathParams, err := router.FindRoute(r)
+
+	// We failed to find a matching route for the request.
+	if err != nil {
+		switch e := err.(type) {
+		case *routers.RouteError:
+			// We've got a bad request, the path requested doesn't match
+			// either server, or path, or something.
+			return errors.New(e.Reason)
+		default:
+			// This should never happen today, but if our upstream code changes,
+			// we don't want to crash the server, so handle the unexpected error.
+			return fmt.Errorf("error validating route: %s", err.Error())
+		}
+	}
+
+	// Validate request
+	requestValidationInput := &openapi3filter.RequestValidationInput{
+		Request:    r,
+		PathParams: pathParams,
+		Route:      route,
+	}
+
+	// Pass the fiber context into the request validator, so that any callbacks
+	// which it invokes make it available.
+	requestContext := context.WithValue(context.Background(), ctxKeyFiberContext{}, c) //nolint:staticcheck
+
+	if options != nil {
+		requestValidationInput.Options = &options.Options
+		requestValidationInput.ParamDecoder = options.ParamDecoder
+		requestContext = context.WithValue(requestContext, ctxKeyUserData{}, options.UserData) //nolint:staticcheck
+	}
+
+	err = openapi3filter.ValidateRequest(requestContext, requestValidationInput)
+	if err != nil {
+		me := openapi3.MultiError{}
+		if errors.As(err, &me) {
+			errFunc := getMultiErrorHandlerFromOptions(options)
+			return errFunc(me)
+		}
+
+		switch e := err.(type) {
+		case *openapi3filter.RequestError:
+			// We've got a bad request
+			// Split up the verbose error by lines and return the first one
+			// openapi errors seem to be multi-line with a decent message on the first
+			errorLines := strings.Split(e.Error(), "\n")
+			return fmt.Errorf("error in openapi3filter.RequestError: %s", errorLines[0])
+		case *openapi3filter.SecurityRequirementsError:
+			return fmt.Errorf("error in openapi3filter.SecurityRequirementsError: %s", e.Error())
+		default:
+			// This should never happen today, but if our upstream code changes,
+			// we don't want to crash the server, so handle the unexpected error.
+			return fmt.Errorf("error validating request: %s", err)
+		}
+	}
+	return nil
+}
+
+// GetFiberContext gets the fiber context from within requests. It returns
+// nil if not found or wrong type.
+func GetFiberContext(c context.Context) *fiber.Ctx {
+	iface := c.Value(ctxKeyFiberContext{})
+	if iface == nil {
+		return nil
+	}
+
+	fiberCtx, ok := iface.(*fiber.Ctx)
+	if ok {
+		return fiberCtx
+	}
+	return nil
+}
+
+func GetUserData(c context.Context) interface{} {
+	return c.Value(ctxKeyUserData{})
+}
+
+// getMultiErrorHandlerFromOptions attempts to get the MultiErrorHandler from the options. If it is not set,
+// return a default handler
+func getMultiErrorHandlerFromOptions(options *Options) MultiErrorHandler {
+	if options == nil {
+		return defaultMultiErrorHandler
+	}
+
+	if options.MultiErrorHandler == nil {
+		return defaultMultiErrorHandler
+	}
+
+	return options.MultiErrorHandler
+}
+
+// defaultMultiErrorHandler returns a StatusBadRequest (400) and a list
+// of all the errors. This method is called if there are no other
+// methods defined on the options.
+func defaultMultiErrorHandler(me openapi3.MultiError) error {
+	return fmt.Errorf("multiple errors encountered: %s", me)
+}
diff --git a/pkg/runtime/bindstring.go b/pkg/runtime/bindstring.go
index 72f74dd..efc9867 100644
--- a/pkg/runtime/bindstring.go
+++ b/pkg/runtime/bindstring.go
@@ -42,7 +42,7 @@ func BindStringToObject(src string, dst interface{}) error {
 		t = v.Type()
 	}
 
-	// For some optioinal args
+	// For some optional args
 	if t.Kind() == reflect.Ptr {
 		if v.IsNil() {
 			v.Set(reflect.New(t.Elem()))
diff --git a/pkg/runtime/deepobject.go b/pkg/runtime/deepobject.go
index 87e9dd9..35286b3 100644
--- a/pkg/runtime/deepobject.go
+++ b/pkg/runtime/deepobject.go
@@ -200,6 +200,19 @@ func assignPathValues(dst interface{}, pathValues fieldOrValue) error {
 	it := iv.Type()
 
 	switch it.Kind() {
+	case reflect.Map:
+		dstMap := reflect.MakeMap(iv.Type())
+		for key, value := range pathValues.fields {
+			dstKey := reflect.ValueOf(key)
+			dstVal := reflect.New(iv.Type().Elem())
+			err := assignPathValues(dstVal.Interface(), value)
+			if err != nil {
+				return fmt.Errorf("error binding map: %w", err)
+			}
+			dstMap.SetMapIndex(dstKey, dstVal.Elem())
+		}
+		iv.Set(dstMap)
+		return nil
 	case reflect.Slice:
 		sliceLength := len(pathValues.fields)
 		dstSlice := reflect.MakeSlice(it, sliceLength, sliceLength)
diff --git a/pkg/runtime/strictmiddleware.go b/pkg/runtime/strictmiddleware.go
new file mode 100644
index 0000000..fac240e
--- /dev/null
+++ b/pkg/runtime/strictmiddleware.go
@@ -0,0 +1,21 @@
+package runtime
+
+import (
+	"context"
+	"net/http"
+
+	"github.com/gin-gonic/gin"
+	"github.com/labstack/echo/v4"
+)
+
+type StrictEchoHandlerFunc func(ctx echo.Context, request interface{}) (response interface{}, err error)
+
+type StrictEchoMiddlewareFunc func(f StrictEchoHandlerFunc, operationID string) StrictEchoHandlerFunc
+
+type StrictHttpHandlerFunc func(ctx context.Context, w http.ResponseWriter, r *http.Request, request interface{}) (response interface{}, err error)
+
+type StrictHttpMiddlewareFunc func(f StrictHttpHandlerFunc, operationID string) StrictHttpHandlerFunc
+
+type StrictGinHandlerFunc func(ctx *gin.Context, request interface{}) (response interface{}, err error)
+
+type StrictGinMiddlewareFunc func(f StrictGinHandlerFunc, operationID string) StrictGinHandlerFunc
diff --git a/pkg/util/inputmapping.go b/pkg/util/inputmapping.go
index fb2ac75..7b475db 100644
--- a/pkg/util/inputmapping.go
+++ b/pkg/util/inputmapping.go
@@ -5,7 +5,7 @@ import (
 	"strings"
 )
 
-// The input mapping is experessed on the command line as `key1:value1,key2:value2,...`
+// The input mapping is expressed on the command line as `key1:value1,key2:value2,...`
 // We parse it here, but need to keep in mind that keys or values may contain
 // commas and colons. We will allow escaping those using double quotes, so
 // when passing in "key1":"value1", we will not look inside the quoted sections.
@@ -47,7 +47,7 @@ func ParseCommandLineList(input string) []string {
 	return args
 }
 
-// This function splits a string along the specifed separator, but it
+// This function splits a string along the specified separator, but it
 // ignores anything between double quotes for splitting. We do simple
 // inside/outside quote counting. Quotes are not stripped from output.
 func splitString(s string, sep rune) []string {


Test patch:
diff --git a/examples/petstore-expanded/fiber/petstore_test.go b/examples/petstore-expanded/fiber/petstore_test.go
new file mode 100644
index 0000000..e6a103a
--- /dev/null
+++ b/examples/petstore-expanded/fiber/petstore_test.go
@@ -0,0 +1,181 @@
+package main
+
+import (
+	"bytes"
+	"encoding/json"
+	"fmt"
+	"net/http"
+	"net/http/httptest"
+	"net/url"
+	"testing"
+
+	"github.com/gofiber/fiber/v2"
+	"github.com/stretchr/testify/assert"
+
+	"github.com/deepmap/oapi-codegen/examples/petstore-expanded/fiber/api"
+)
+
+func doGet(t *testing.T, app *fiber.App, rawURL string) (*http.Response, error) {
+
+	u, err := url.Parse(rawURL)
+	if err != nil {
+		t.Fatalf("Invalid url: %s", rawURL)
+	}
+
+	req := httptest.NewRequest("GET", u.RequestURI(), nil)
+	req.Header.Add("Accept", "application/json")
+	req.Host = u.Host
+
+	return app.Test(req)
+}
+
+func doPost(t *testing.T, app *fiber.App, rawURL string, jsonBody interface{}) (*http.Response, error) {
+	u, err := url.Parse(rawURL)
+	if err != nil {
+		t.Fatalf("Invalid url: %s", rawURL)
+	}
+
+	buf, err := json.Marshal(jsonBody)
+	if err != nil {
+		return nil, err
+	}
+	req := httptest.NewRequest("POST", u.RequestURI(), bytes.NewReader(buf))
+	req.Header.Add("Accept", "application/json")
+	req.Header.Add("Content-Type", "application/json")
+	req.Host = u.Host
+	return app.Test(req)
+}
+
+func doDelete(t *testing.T, app *fiber.App, rawURL string) (*http.Response, error) {
+	u, err := url.Parse(rawURL)
+	if err != nil {
+		t.Fatalf("Invalid url: %s", rawURL)
+	}
+
+	req := httptest.NewRequest("DELETE", u.RequestURI(), nil)
+	req.Header.Add("Accept", "application/json")
+	req.Host = u.Host
+	return app.Test(req)
+}
+
+func TestPetStore(t *testing.T) {
+	var err error
+	store := api.NewPetStore()
+	fiberPetServer := NewFiberPetServer(store)
+
+	t.Run("Add pet", func(t *testing.T) {
+		tag := "TagOfSpot"
+		newPet := api.NewPet{
+			Name: "Spot",
+			Tag:  &tag,
+		}
+
+		rr, _ := doPost(t, fiberPetServer, "/pets", newPet)
+		assert.Equal(t, http.StatusCreated, rr.StatusCode)
+
+		var resultPet api.Pet
+		err = json.NewDecoder(rr.Body).Decode(&resultPet)
+		assert.NoError(t, err, "error unmarshaling response")
+		assert.Equal(t, newPet.Name, resultPet.Name)
+		assert.Equal(t, *newPet.Tag, *resultPet.Tag)
+	})
+
+	t.Run("Find pet by ID", func(t *testing.T) {
+		pet := api.Pet{
+			Id: 100,
+		}
+
+		store.Pets[pet.Id] = pet
+		rr, _ := doGet(t, fiberPetServer, fmt.Sprintf("/pets/%d", pet.Id))
+		assert.Equal(t, http.StatusOK, rr.StatusCode)
+
+		var resultPet api.Pet
+		err = json.NewDecoder(rr.Body).Decode(&resultPet)
+		assert.NoError(t, err, "error getting pet")
+		assert.Equal(t, pet, resultPet)
+	})
+
+	t.Run("Pet not found", func(t *testing.T) {
+		rr, _ := doGet(t, fiberPetServer, "/pets/27179095781")
+		assert.Equal(t, http.StatusNotFound, rr.StatusCode)
+
+		var petError api.Error
+		err = json.NewDecoder(rr.Body).Decode(&petError)
+		assert.NoError(t, err, "error getting response", err)
+		assert.Equal(t, int32(http.StatusNotFound), petError.Code)
+	})
+
+	t.Run("List all pets", func(t *testing.T) {
+		store.Pets = map[int64]api.Pet{1: {}, 2: {}}
+
+		// Now, list all pets, we should have two
+		rr, _ := doGet(t, fiberPetServer, "/pets")
+		assert.Equal(t, http.StatusOK, rr.StatusCode)
+
+		var petList []api.Pet
+		err = json.NewDecoder(rr.Body).Decode(&petList)
+		assert.NoError(t, err, "error getting response", err)
+		assert.Equal(t, 2, len(petList))
+	})
+
+	t.Run("Filter pets by tag", func(t *testing.T) {
+		tag := "TagOfFido"
+
+		store.Pets = map[int64]api.Pet{
+			1: {
+				Tag: &tag,
+			},
+			2: {},
+		}
+
+		// Filter pets by tag, we should have 1
+		rr, _ := doGet(t, fiberPetServer, "/pets?tags=TagOfFido")
+		assert.Equal(t, http.StatusOK, rr.StatusCode)
+
+		var petList []api.Pet
+		err = json.NewDecoder(rr.Body).Decode(&petList)
+		assert.NoError(t, err, "error getting response", err)
+		assert.Equal(t, 1, len(petList))
+	})
+
+	t.Run("Filter pets by tag", func(t *testing.T) {
+		store.Pets = map[int64]api.Pet{1: {}, 2: {}}
+
+		// Filter pets by non existent tag, we should have 0
+		rr, _ := doGet(t, fiberPetServer, "/pets?tags=NotExists")
+		assert.Equal(t, http.StatusOK, rr.StatusCode)
+
+		var petList []api.Pet
+		err = json.NewDecoder(rr.Body).Decode(&petList)
+		assert.NoError(t, err, "error getting response", err)
+		assert.Equal(t, 0, len(petList))
+	})
+
+	t.Run("Delete pets", func(t *testing.T) {
+		store.Pets = map[int64]api.Pet{1: {}, 2: {}}
+
+		// Let's delete non-existent pet
+		rr, _ := doDelete(t, fiberPetServer, "/pets/7")
+		assert.Equal(t, http.StatusNotFound, rr.StatusCode)
+
+		var petError api.Error
+		err = json.NewDecoder(rr.Body).Decode(&petError)
+		assert.NoError(t, err, "error unmarshaling PetError")
+		assert.Equal(t, int32(http.StatusNotFound), petError.Code)
+
+		// Now, delete both real pets
+		rr, _ = doDelete(t, fiberPetServer, "/pets/1")
+		assert.Equal(t, http.StatusNoContent, rr.StatusCode)
+
+		rr, _ = doDelete(t, fiberPetServer, "/pets/2")
+		assert.Equal(t, http.StatusNoContent, rr.StatusCode)
+
+		// Should have no pets left.
+		var petList []api.Pet
+		rr, _ = doGet(t, fiberPetServer, "/pets")
+		assert.Equal(t, http.StatusOK, rr.StatusCode)
+		err = json.NewDecoder(rr.Body).Decode(&petList)
+		assert.NoError(t, err, "error getting response", err)
+		assert.Equal(t, 0, len(petList))
+	})
+}
diff --git a/internal/test/any_of/param/param.gen.go b/internal/test/any_of/param/param.gen.go
index 07dd59b..650f1f3 100644
--- a/internal/test/any_of/param/param.gen.go
+++ b/internal/test/any_of/param/param.gen.go
@@ -281,42 +281,44 @@ func NewGetTestRequest(server string, params *GetTestParams) (*http.Request, err
 		return nil, err
 	}
 
-	queryValues := queryURL.Query()
-
-	if params.Test != nil {
-
-		if queryFrag, err := runtime.StyleParamWithLocation("form", true, "test", runtime.ParamLocationQuery, *params.Test); err != nil {
-			return nil, err
-		} else if parsed, err := url.ParseQuery(queryFrag); err != nil {
-			return nil, err
-		} else {
-			for k, v := range parsed {
-				for _, v2 := range v {
-					queryValues.Add(k, v2)
+	if params != nil {
+		queryValues := queryURL.Query()
+
+		if params.Test != nil {
+
+			if queryFrag, err := runtime.StyleParamWithLocation("form", true, "test", runtime.ParamLocationQuery, *params.Test); err != nil {
+				return nil, err
+			} else if parsed, err := url.ParseQuery(queryFrag); err != nil {
+				return nil, err
+			} else {
+				for k, v := range parsed {
+					for _, v2 := range v {
+						queryValues.Add(k, v2)
+					}
 				}
 			}
-		}
-
-	}
 
-	if params.Test2 != nil {
+		}
 
-		if queryFrag, err := runtime.StyleParamWithLocation("form", true, "test2", runtime.ParamLocationQuery, *params.Test2); err != nil {
-			return nil, err
-		} else if parsed, err := url.ParseQuery(queryFrag); err != nil {
-			return nil, err
-		} else {
-			for k, v := range parsed {
-				for _, v2 := range v {
-					queryValues.Add(k, v2)
+		if params.Test2 != nil {
+
+			if queryFrag, err := runtime.StyleParamWithLocation("form", true, "test2", runtime.ParamLocationQuery, *params.Test2); err != nil {
+				return nil, err
+			} else if parsed, err := url.ParseQuery(queryFrag); err != nil {
+				return nil, err
+			} else {
+				for k, v := range parsed {
+					for _, v2 := range v {
+						queryValues.Add(k, v2)
+					}
 				}
 			}
+
 		}
 
+		queryURL.RawQuery = queryValues.Encode()
 	}
 
-	queryURL.RawQuery = queryValues.Encode()
-
 	req, err := http.NewRequest("GET", queryURL.String(), nil)
 	if err != nil {
 		return nil, err
diff --git a/internal/test/components/components.gen.go b/internal/test/components/components.gen.go
index be29e17..3aa19c2 100644
--- a/internal/test/components/components.gen.go
+++ b/internal/test/components/components.gen.go
@@ -131,6 +131,9 @@ type AdditionalPropertiesObject5 map[string]SchemaObject
 // AdditionalPropertiesObject6 Array of object with additional properties
 type AdditionalPropertiesObject6 = []map[string]SchemaObject
 
+// AdditionalPropertiesObject7 Has additional properties with schema for dictionaries
+type AdditionalPropertiesObject7 map[string]*SchemaObjectNullable
+
 // AnyOfObject1 simple anyOf case
 type AnyOfObject1 struct {
 	union json.RawMessage
@@ -347,6 +350,17 @@ type SchemaObject struct {
 	WriteOnlyRequiredProp *int    `json:"writeOnlyRequiredProp,omitempty"`
 }
 
+// SchemaObjectNullable defines model for SchemaObjectNullable.
+type SchemaObjectNullable struct {
+	FirstName string `json:"firstName"`
+
+	// ReadOnlyRequiredProp This property is required and readOnly, so the go model should have it as a pointer,
+	// as it will not be included when it is sent from client to server.
+	ReadOnlyRequiredProp  *string `json:"readOnlyRequiredProp,omitempty"`
+	Role                  string  `json:"role"`
+	WriteOnlyRequiredProp *int    `json:"writeOnlyRequiredProp,omitempty"`
+}
+
 // OneOfVariant51 defines model for one_of_variant51.
 type OneOfVariant51 struct {
 	Discriminator string `json:"discriminator"`
diff --git a/internal/test/components/components.yaml b/internal/test/components/components.yaml
index 24fdf77..6e2695b 100644
--- a/internal/test/components/components.yaml
+++ b/internal/test/components/components.yaml
@@ -137,7 +137,7 @@ components:
           type: string
         readOnlyRequiredProp:
           description: |
-            This property is required and readOnly, so the go model should have it as a pointer, 
+            This property is required and readOnly, so the go model should have it as a pointer,
             as it will not be included when it is sent from client to server.
           type: string
           readOnly: true
@@ -149,6 +149,27 @@ components:
         - firstName
         - readOnlyRequiredProp
         - writeOnlyRequiredProp
+    SchemaObjectNullable:
+      properties:
+        role:
+          type: string
+        firstName:
+          type: string
+        readOnlyRequiredProp:
+          description: |
+            This property is required and readOnly, so the go model should have it as a pointer,
+            as it will not be included when it is sent from client to server.
+          type: string
+          readOnly: true
+        writeOnlyRequiredProp:
+          type: integer
+          writeOnly: true
+      required:
+        - role
+        - firstName
+        - readOnlyRequiredProp
+        - writeOnlyRequiredProp
+      nullable: true
     AdditionalPropertiesObject1:
       description: Has additional properties of type int
       type: object
@@ -207,6 +228,11 @@ components:
         type: object
         additionalProperties:
           $ref: '#/components/schemas/SchemaObject'
+    AdditionalPropertiesObject7:
+      description: Has additional properties with schema for dictionaries
+      type: object
+      additionalProperties:
+        $ref: '#/components/schemas/SchemaObjectNullable'
     OneOfObject1:
       description: oneOf with references and no discriminator
       oneOf:
diff --git a/internal/test/components/components_test.go b/internal/test/components/components_test.go
index 7810ea2..7e187a5 100644
--- a/internal/test/components/components_test.go
+++ b/internal/test/components/components_test.go
@@ -63,6 +63,20 @@ func TestAdditionalProperties(t *testing.T) {
 	err = json.Unmarshal([]byte(buf2), &obj5)
 	assert.NoError(t, err)
 	assert.Equal(t, bossSchema, obj5["boss"])
+
+	bossSchemaNullable := &SchemaObjectNullable{
+		FirstName: "bob",
+		Role:      "warehouse manager",
+	}
+
+	buf3 := `{"boss": { "firstName": "bob", "role": "warehouse manager" }, "employee": null}`
+	var obj7 AdditionalPropertiesObject7
+	err = json.Unmarshal([]byte(buf3), &obj7)
+	assert.NoError(t, err)
+	employee, ok := obj7["employee"]
+	assert.True(t, ok)
+	assert.Equal(t, bossSchemaNullable, obj7["boss"])
+	assert.Nil(t, employee)
 }
 
 func TestOneOf(t *testing.T) {
diff --git a/internal/test/issues/issue-head-digit-of-httpheader/issue.gen.go b/internal/test/issues/issue-head-digit-of-httpheader/issue.gen.go
index 703c262..6e5994e 100644
--- a/internal/test/issues/issue-head-digit-of-httpheader/issue.gen.go
+++ b/internal/test/issues/issue-head-digit-of-httpheader/issue.gen.go
@@ -3,37 +3,9 @@
 // Code generated by github.com/deepmap/oapi-codegen version (devel) DO NOT EDIT.
 package headdigitofhttpheader
 
-import (
-	"context"
-	"fmt"
-	"net/http"
-)
-
 type N200ResponseHeaders struct {
 	N000Foo string
 }
 type N200Response struct {
 	Headers N200ResponseHeaders
 }
-
-type GetFooRequestObject struct {
-}
-
-type GetFooResponseObject interface {
-	VisitGetFooResponse(w http.ResponseWriter) error
-}
-
-type GetFoo200Response = N200Response
-
-func (response GetFoo200Response) VisitGetFooResponse(w http.ResponseWriter) error {
-	w.Header().Set("000-foo", fmt.Sprint(response.Headers.N000Foo))
-	w.WriteHeader(200)
-	return nil
-}
-
-// StrictServerInterface represents all server handlers.
-type StrictServerInterface interface {
-
-	// (GET /foo)
-	GetFoo(ctx context.Context, request GetFooRequestObject) (GetFooResponseObject, error)
-}
diff --git a/internal/test/issues/issue-head-digit-of-operation-id/issue.gen.go b/internal/test/issues/issue-head-digit-of-operation-id/issue.gen.go
index 1760746..4a5d3e8 100644
--- a/internal/test/issues/issue-head-digit-of-operation-id/issue.gen.go
+++ b/internal/test/issues/issue-head-digit-of-operation-id/issue.gen.go
@@ -2,22 +2,3 @@
 //
 // Code generated by github.com/deepmap/oapi-codegen version (devel) DO NOT EDIT.
 package head_digit_of_operation_id
-
-import (
-	"context"
-	"net/http"
-)
-
-type N3GPPFooRequestObject struct {
-}
-
-type N3GPPFooResponseObject interface {
-	VisitN3GPPFooResponse(w http.ResponseWriter) error
-}
-
-// StrictServerInterface represents all server handlers.
-type StrictServerInterface interface {
-
-	// (GET /3gpp/foo)
-	N3GPPFoo(ctx context.Context, request N3GPPFooRequestObject) (N3GPPFooResponseObject, error)
-}
diff --git a/internal/test/issues/issue-removed-external-ref/gen/spec_base/issue.gen.go b/internal/test/issues/issue-removed-external-ref/gen/spec_base/issue.gen.go
index 09e8df3..69766e1 100644
--- a/internal/test/issues/issue-removed-external-ref/gen/spec_base/issue.gen.go
+++ b/internal/test/issues/issue-removed-external-ref/gen/spec_base/issue.gen.go
@@ -10,6 +10,7 @@ import (
 	"net/http"
 
 	externalRef0 "github.com/deepmap/oapi-codegen/internal/test/issues/issue-removed-external-ref/gen/spec_ext"
+	"github.com/deepmap/oapi-codegen/pkg/runtime"
 	"github.com/go-chi/chi/v5"
 )
 
@@ -244,9 +245,8 @@ type StrictServerInterface interface {
 	PostNoTrouble(ctx context.Context, request PostNoTroubleRequestObject) (PostNoTroubleResponseObject, error)
 }
 
-type StrictHandlerFunc func(ctx context.Context, w http.ResponseWriter, r *http.Request, args interface{}) (interface{}, error)
-
-type StrictMiddlewareFunc func(f StrictHandlerFunc, operationID string) StrictHandlerFunc
+type StrictHandlerFunc = runtime.StrictHttpHandlerFunc
+type StrictMiddlewareFunc = runtime.StrictHttpMiddlewareFunc
 
 type StrictHTTPServerOptions struct {
 	RequestErrorHandlerFunc  func(w http.ResponseWriter, r *http.Request, err error)
diff --git a/internal/test/issues/issue-removed-external-ref/gen/spec_ext/issue.gen.go b/internal/test/issues/issue-removed-external-ref/gen/spec_ext/issue.gen.go
index 21222f8..ed5a474 100644
--- a/internal/test/issues/issue-removed-external-ref/gen/spec_ext/issue.gen.go
+++ b/internal/test/issues/issue-removed-external-ref/gen/spec_ext/issue.gen.go
@@ -4,10 +4,10 @@
 package spec_ext
 
 import (
-	"context"
 	"fmt"
 	"net/http"
 
+	"github.com/deepmap/oapi-codegen/pkg/runtime"
 	"github.com/go-chi/chi/v5"
 )
 
@@ -161,9 +161,8 @@ type PascalJSONResponse PascalSchema
 type StrictServerInterface interface {
 }
 
-type StrictHandlerFunc func(ctx context.Context, w http.ResponseWriter, r *http.Request, args interface{}) (interface{}, error)
-
-type StrictMiddlewareFunc func(f StrictHandlerFunc, operationID string) StrictHandlerFunc
+type StrictHandlerFunc = runtime.StrictHttpHandlerFunc
+type StrictMiddlewareFunc = runtime.StrictHttpMiddlewareFunc
 
 type StrictHTTPServerOptions struct {
 	RequestErrorHandlerFunc  func(w http.ResponseWriter, r *http.Request, err error)
diff --git a/internal/test/parameters/parameters.gen.go b/internal/test/parameters/parameters.gen.go
index 45d11b4..1f2b4b8 100644
--- a/internal/test/parameters/parameters.gen.go
+++ b/internal/test/parameters/parameters.gen.go
@@ -732,26 +732,28 @@ func NewEnumParamsRequest(server string, params *EnumParamsParams) (*http.Reques
 		return nil, err
 	}
 
-	queryValues := queryURL.Query()
-
-	if params.EnumPathParam != nil {
-
-		if queryFrag, err := runtime.StyleParamWithLocation("form", true, "enumPathParam", runtime.ParamLocationQuery, *params.EnumPathParam); err != nil {
-			return nil, err
-		} else if parsed, err := url.ParseQuery(queryFrag); err != nil {
-			return nil, err
-		} else {
-			for k, v := range parsed {
-				for _, v2 := range v {
-					queryValues.Add(k, v2)
+	if params != nil {
+		queryValues := queryURL.Query()
+
+		if params.EnumPathParam != nil {
+
+			if queryFrag, err := runtime.StyleParamWithLocation("form", true, "enumPathParam", runtime.ParamLocationQuery, *params.EnumPathParam); err != nil {
+				return nil, err
+			} else if parsed, err := url.ParseQuery(queryFrag); err != nil {
+				return nil, err
+			} else {
+				for k, v := range parsed {
+					for _, v2 := range v {
+						queryValues.Add(k, v2)
+					}
 				}
 			}
+
 		}
 
+		queryURL.RawQuery = queryValues.Encode()
 	}
 
-	queryURL.RawQuery = queryValues.Encode()
-
 	req, err := http.NewRequest("GET", queryURL.String(), nil)
 	if err != nil {
 		return nil, err
@@ -1199,21 +1201,23 @@ func NewGetDeepObjectRequest(server string, params *GetDeepObjectParams) (*http.
 		return nil, err
 	}
 
-	queryValues := queryURL.Query()
+	if params != nil {
+		queryValues := queryURL.Query()
 
-	if queryFrag, err := runtime.StyleParamWithLocation("deepObject", true, "deepObj", runtime.ParamLocationQuery, params.DeepObj); err != nil {
-		return nil, err
-	} else if parsed, err := url.ParseQuery(queryFrag); err != nil {
-		return nil, err
-	} else {
-		for k, v := range parsed {
-			for _, v2 := range v {
-				queryValues.Add(k, v2)
+		if queryFrag, err := runtime.StyleParamWithLocation("deepObject", true, "deepObj", runtime.ParamLocationQuery, params.DeepObj); err != nil {
+			return nil, err
+		} else if parsed, err := url.ParseQuery(queryFrag); err != nil {
+			return nil, err
+		} else {
+			for k, v := range parsed {
+				for _, v2 := range v {
+					queryValues.Add(k, v2)
+				}
 			}
 		}
-	}
 
-	queryURL.RawQuery = queryValues.Encode()
+		queryURL.RawQuery = queryValues.Encode()
+	}
 
 	req, err := http.NewRequest("GET", queryURL.String(), nil)
 	if err != nil {
@@ -1242,148 +1246,150 @@ func NewGetQueryFormRequest(server string, params *GetQueryFormParams) (*http.Re
 		return nil, err
 	}
 
-	queryValues := queryURL.Query()
+	if params != nil {
+		queryValues := queryURL.Query()
 
-	if params.Ea != nil {
+		if params.Ea != nil {
 
-		if queryFrag, err := runtime.StyleParamWithLocation("form", true, "ea", runtime.ParamLocationQuery, *params.Ea); err != nil {
-			return nil, err
-		} else if parsed, err := url.ParseQuery(queryFrag); err != nil {
-			return nil, err
-		} else {
-			for k, v := range parsed {
-				for _, v2 := range v {
-					queryValues.Add(k, v2)
+			if queryFrag, err := runtime.StyleParamWithLocation("form", true, "ea", runtime.ParamLocationQuery, *params.Ea); err != nil {
+				return nil, err
+			} else if parsed, err := url.ParseQuery(queryFrag); err != nil {
+				return nil, err
+			} else {
+				for k, v := range parsed {
+					for _, v2 := range v {
+						queryValues.Add(k, v2)
+					}
 				}
 			}
-		}
 
-	}
+		}
 
-	if params.A != nil {
+		if params.A != nil {
 
-		if queryFrag, err := runtime.StyleParamWithLocation("form", false, "a", runtime.ParamLocationQuery, *params.A); err != nil {
-			return nil, err
-		} else if parsed, err := url.ParseQuery(queryFrag); err != nil {
-			return nil, err
-		} else {
-			for k, v := range parsed {
-				for _, v2 := range v {
-					queryValues.Add(k, v2)
+			if queryFrag, err := runtime.StyleParamWithLocation("form", false, "a", runtime.ParamLocationQuery, *params.A); err != nil {
+				return nil, err
+			} else if parsed, err := url.ParseQuery(queryFrag); err != nil {
+				return nil, err
+			} else {
+				for k, v := range parsed {
+					for _, v2 := range v {
+						queryValues.Add(k, v2)
+					}
 				}
 			}
-		}
 
-	}
+		}
 
-	if params.Eo != nil {
+		if params.Eo != nil {
 
-		if queryFrag, err := runtime.StyleParamWithLocation("form", true, "eo", runtime.ParamLocationQuery, *params.Eo); err != nil {
-			return nil, err
-		} else if parsed, err := url.ParseQuery(queryFrag); err != nil {
-			return nil, err
-		} else {
-			for k, v := range parsed {
-				for _, v2 := range v {
-					queryValues.Add(k, v2)
+			if queryFrag, err := runtime.StyleParamWithLocation("form", true, "eo", runtime.ParamLocationQuery, *params.Eo); err != nil {
+				return nil, err
+			} else if parsed, err := url.ParseQuery(queryFrag); err != nil {
+				return nil, err
+			} else {
+				for k, v := range parsed {
+					for _, v2 := range v {
+						queryValues.Add(k, v2)
+					}
 				}
 			}
-		}
 
-	}
+		}
 
-	if params.O != nil {
+		if params.O != nil {
 
-		if queryFrag, err := runtime.StyleParamWithLocation("form", false, "o", runtime.ParamLocationQuery, *params.O); err != nil {
-			return nil, err
-		} else if parsed, err := url.ParseQuery(queryFrag); err != nil {
-			return nil, err
-		} else {
-			for k, v := range parsed {
-				for _, v2 := range v {
-					queryValues.Add(k, v2)
+			if queryFrag, err := runtime.StyleParamWithLocation("form", false, "o", runtime.ParamLocationQuery, *params.O); err != nil {
+				return nil, err
+			} else if parsed, err := url.ParseQuery(queryFrag); err != nil {
+				return nil, err
+			} else {
+				for k, v := range parsed {
+					for _, v2 := range v {
+						queryValues.Add(k, v2)
+					}
 				}
 			}
-		}
 
-	}
+		}
 
-	if params.Ep != nil {
+		if params.Ep != nil {
 
-		if queryFrag, err := runtime.StyleParamWithLocation("form", true, "ep", runtime.ParamLocationQuery, *params.Ep); err != nil {
-			return nil, err
-		} else if parsed, err := url.ParseQuery(queryFrag); err != nil {
-			return nil, err
-		} else {
-			for k, v := range parsed {
-				for _, v2 := range v {
-					queryValues.Add(k, v2)
+			if queryFrag, err := runtime.StyleParamWithLocation("form", true, "ep", runtime.ParamLocationQuery, *params.Ep); err != nil {
+				return nil, err
+			} else if parsed, err := url.ParseQuery(queryFrag); err != nil {
+				return nil, err
+			} else {
+				for k, v := range parsed {
+					for _, v2 := range v {
+						queryValues.Add(k, v2)
+					}
 				}
 			}
-		}
 
-	}
+		}
 
-	if params.P != nil {
+		if params.P != nil {
 
-		if queryFrag, err := runtime.StyleParamWithLocation("form", false, "p", runtime.ParamLocationQuery, *params.P); err != nil {
-			return nil, err
-		} else if parsed, err := url.ParseQuery(queryFrag); err != nil {
-			return nil, err
-		} else {
-			for k, v := range parsed {
-				for _, v2 := range v {
-					queryValues.Add(k, v2)
+			if queryFrag, err := runtime.StyleParamWithLocation("form", false, "p", runtime.ParamLocationQuery, *params.P); err != nil {
+				return nil, err
+			} else if parsed, err := url.ParseQuery(queryFrag); err != nil {
+				return nil, err
+			} else {
+				for k, v := range parsed {
+					for _, v2 := range v {
+						queryValues.Add(k, v2)
+					}
 				}
 			}
-		}
 
-	}
+		}
 
-	if params.Ps != nil {
+		if params.Ps != nil {
 
-		if queryFrag, err := runtime.StyleParamWithLocation("form", true, "ps", runtime.ParamLocationQuery, *params.Ps); err != nil {
-			return nil, err
-		} else if parsed, err := url.ParseQuery(queryFrag); err != nil {
-			return nil, err
-		} else {
-			for k, v := range parsed {
-				for _, v2 := range v {
-					queryValues.Add(k, v2)
+			if queryFrag, err := runtime.StyleParamWithLocation("form", true, "ps", runtime.ParamLocationQuery, *params.Ps); err != nil {
+				return nil, err
+			} else if parsed, err := url.ParseQuery(queryFrag); err != nil {
+				return nil, err
+			} else {
+				for k, v := range parsed {
+					for _, v2 := range v {
+						queryValues.Add(k, v2)
+					}
 				}
 			}
+
 		}
 
-	}
+		if params.Co != nil {
 
-	if params.Co != nil {
+			if queryParamBuf, err := json.Marshal(*params.Co); err != nil {
+				return nil, err
+			} else {
+				queryValues.Add("co", string(queryParamBuf))
+			}
 
-		if queryParamBuf, err := json.Marshal(*params.Co); err != nil {
-			return nil, err
-		} else {
-			queryValues.Add("co", string(queryParamBuf))
 		}
 
-	}
+		if params.N1s != nil {
 
-	if params.N1s != nil {
-
-		if queryFrag, err := runtime.StyleParamWithLocation("form", true, "1s", runtime.ParamLocationQuery, *params.N1s); err != nil {
-			return nil, err
-		} else if parsed, err := url.ParseQuery(queryFrag); err != nil {
-			return nil, err
-		} else {
-			for k, v := range parsed {
-				for _, v2 := range v {
-					queryValues.Add(k, v2)
+			if queryFrag, err := runtime.StyleParamWithLocation("form", true, "1s", runtime.ParamLocationQuery, *params.N1s); err != nil {
+				return nil, err
+			} else if parsed, err := url.ParseQuery(queryFrag); err != nil {
+				return nil, err
+			} else {
+				for k, v := range parsed {
+					for _, v2 := range v {
+						queryValues.Add(k, v2)
+					}
 				}
 			}
+
 		}
 
+		queryURL.RawQuery = queryValues.Encode()
 	}
 
-	queryURL.RawQuery = queryValues.Encode()
-
 	req, err := http.NewRequest("GET", queryURL.String(), nil)
 	if err != nil {
 		return nil, err
diff --git a/internal/test/schemas/config.yaml b/internal/test/schemas/config.yaml
index 58b5060..9086953 100644
--- a/internal/test/schemas/config.yaml
+++ b/internal/test/schemas/config.yaml
@@ -5,3 +5,5 @@ generate:
   models: true
   embedded-spec: true
 output: schemas.gen.go
+output-options:
+  skip-prune: true
diff --git a/internal/test/schemas/schemas.gen.go b/internal/test/schemas/schemas.gen.go
index d1791ab..91f7338 100644
--- a/internal/test/schemas/schemas.gen.go
+++ b/internal/test/schemas/schemas.gen.go
@@ -85,6 +85,17 @@ type NullableProperties struct {
 	RequiredAndNullable *string `json:"requiredAndNullable"`
 }
 
+// OuterTypeWithAnonymousInner defines model for OuterTypeWithAnonymousInner.
+type OuterTypeWithAnonymousInner struct {
+	Inner InnerRenamedAnonymousObject `json:"inner"`
+	Name  string                      `json:"name"`
+}
+
+// InnerRenamedAnonymousObject defines model for .
+type InnerRenamedAnonymousObject struct {
+	Id int `json:"id"`
+}
+
 // StringInPath defines model for StringInPath.
 type StringInPath = string
 
@@ -178,6 +189,9 @@ type ClientInterface interface {
 	// EnsureEverythingIsReferenced request
 	EnsureEverythingIsReferenced(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error)
 
+	// Issue1051 request
+	Issue1051(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error)
+
 	// Issue127 request
 	Issue127(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error)
 
@@ -219,6 +233,18 @@ func (c *Client) EnsureEverythingIsReferenced(ctx context.Context, reqEditors ..
 	return c.Client.Do(req)
 }
 
+func (c *Client) Issue1051(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) {
+	req, err := NewIssue1051Request(c.Server)
+	if err != nil {
+		return nil, err
+	}
+	req = req.WithContext(ctx)
+	if err := c.applyEditors(ctx, req, reqEditors); err != nil {
+		return nil, err
+	}
+	return c.Client.Do(req)
+}
+
 func (c *Client) Issue127(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) {
 	req, err := NewIssue127Request(c.Server)
 	if err != nil {
@@ -366,6 +392,33 @@ func NewEnsureEverythingIsReferencedRequest(server string) (*http.Request, error
 	return req, nil
 }
 
+// NewIssue1051Request generates requests for Issue1051
+func NewIssue1051Request(server string) (*http.Request, error) {
+	var err error
+
+	serverURL, err := url.Parse(server)
+	if err != nil {
+		return nil, err
+	}
+
+	operationPath := fmt.Sprintf("/issues/1051")
+	if operationPath[0] == '/' {
+		operationPath = "." + operationPath
+	}
+
+	queryURL, err := serverURL.Parse(operationPath)
+	if err != nil {
+		return nil, err
+	}
+
+	req, err := http.NewRequest("GET", queryURL.String(), nil)
+	if err != nil {
+		return nil, err
+	}
+
+	return req, nil
+}
+
 // NewIssue127Request generates requests for Issue127
 func NewIssue127Request(server string) (*http.Request, error) {
 	var err error
@@ -592,21 +645,23 @@ func NewIssue9RequestWithBody(server string, params *Issue9Params, contentType s
 		return nil, err
 	}
 
-	queryValues := queryURL.Query()
+	if params != nil {
+		queryValues := queryURL.Query()
 
-	if queryFrag, err := runtime.StyleParamWithLocation("form", true, "foo", runtime.ParamLocationQuery, params.Foo); err != nil {
-		return nil, err
-	} else if parsed, err := url.ParseQuery(queryFrag); err != nil {
-		return nil, err
-	} else {
-		for k, v := range parsed {
-			for _, v2 := range v {
-				queryValues.Add(k, v2)
+		if queryFrag, err := runtime.StyleParamWithLocation("form", true, "foo", runtime.ParamLocationQuery, params.Foo); err != nil {
+			return nil, err
+		} else if parsed, err := url.ParseQuery(queryFrag); err != nil {
+			return nil, err
+		} else {
+			for k, v := range parsed {
+				for _, v2 := range v {
+					queryValues.Add(k, v2)
+				}
 			}
 		}
-	}
 
-	queryURL.RawQuery = queryValues.Encode()
+		queryURL.RawQuery = queryValues.Encode()
+	}
 
 	req, err := http.NewRequest("GET", queryURL.String(), body)
 	if err != nil {
@@ -691,6 +746,9 @@ type ClientWithResponsesInterface interface {
 	// EnsureEverythingIsReferenced request
 	EnsureEverythingIsReferencedWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*EnsureEverythingIsReferencedResponse, error)
 
+	// Issue1051 request
+	Issue1051WithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*Issue1051Response, error)
+
 	// Issue127 request
 	Issue127WithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*Issue127Response, error)
 
@@ -750,6 +808,29 @@ func (r EnsureEverythingIsReferencedResponse) StatusCode() int {
 	return 0
 }
 
+type Issue1051Response struct {
+	Body                             []byte
+	HTTPResponse                     *http.Response
+	JSON200                          *map[string]interface{}
+	ApplicationvndSomethingV1JSON200 *map[string]interface{}
+}
+
+// Status returns HTTPResponse.Status
+func (r Issue1051Response) Status() string {
+	if r.HTTPResponse != nil {
+		return r.HTTPResponse.Status
+	}
+	return http.StatusText(0)
+}
+
+// StatusCode returns HTTPResponse.StatusCode
+func (r Issue1051Response) StatusCode() int {
+	if r.HTTPResponse != nil {
+		return r.HTTPResponse.StatusCode
+	}
+	return 0
+}
+
 type Issue127Response struct {
 	Body         []byte
 	HTTPResponse *http.Response
@@ -933,6 +1014,15 @@ func (c *ClientWithResponses) EnsureEverythingIsReferencedWithResponse(ctx conte
 	return ParseEnsureEverythingIsReferencedResponse(rsp)
 }
 
+// Issue1051WithResponse request returning *Issue1051Response
+func (c *ClientWithResponses) Issue1051WithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*Issue1051Response, error) {
+	rsp, err := c.Issue1051(ctx, reqEditors...)
+	if err != nil {
+		return nil, err
+	}
+	return ParseIssue1051Response(rsp)
+}
+
 // Issue127WithResponse request returning *Issue127Response
 func (c *ClientWithResponses) Issue127WithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*Issue127Response, error) {
 	rsp, err := c.Issue127(ctx, reqEditors...)
@@ -1055,6 +1145,32 @@ func ParseEnsureEverythingIsReferencedResponse(rsp *http.Response) (*EnsureEvery
 	return response, nil
 }
 
+// ParseIssue1051Response parses an HTTP response from a Issue1051WithResponse call
+func ParseIssue1051Response(rsp *http.Response) (*Issue1051Response, error) {
+	bodyBytes, err := io.ReadAll(rsp.Body)
+	defer func() { _ = rsp.Body.Close() }()
+	if err != nil {
+		return nil, err
+	}
+
+	response := &Issue1051Response{
+		Body:         bodyBytes,
+		HTTPResponse: rsp,
+	}
+
+	switch {
+	case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200:
+		var dest map[string]interface{}
+		if err := json.Unmarshal(bodyBytes, &dest); err != nil {
+			return nil, err
+		}
+		response.ApplicationvndSomethingV1JSON200 = &dest
+
+	}
+
+	return response, nil
+}
+
 // ParseIssue127Response parses an HTTP response from a Issue127WithResponse call
 func ParseIssue127Response(rsp *http.Response) (*Issue127Response, error) {
 	bodyBytes, err := io.ReadAll(rsp.Body)
@@ -1246,6 +1362,9 @@ type ServerInterface interface {
 	// (GET /ensure-everything-is-referenced)
 	EnsureEverythingIsReferenced(ctx echo.Context) error
 
+	// (GET /issues/1051)
+	Issue1051(ctx echo.Context) error
+
 	// (GET /issues/127)
 	Issue127(ctx echo.Context) error
 
@@ -1287,6 +1406,17 @@ func (w *ServerInterfaceWrapper) EnsureEverythingIsReferenced(ctx echo.Context)
 	return err
 }
 
+// Issue1051 converts echo context to params.
+func (w *ServerInterfaceWrapper) Issue1051(ctx echo.Context) error {
+	var err error
+
+	ctx.Set(Access_tokenScopes, []string{})
+
+	// Invoke the callback with all the unmarshalled arguments
+	err = w.Handler.Issue1051(ctx)
+	return err
+}
+
 // Issue127 converts echo context to params.
 func (w *ServerInterfaceWrapper) Issue127(ctx echo.Context) error {
 	var err error
@@ -1434,6 +1564,7 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL
 	}
 
 	router.GET(baseURL+"/ensure-everything-is-referenced", wrapper.EnsureEverythingIsReferenced)
+	router.GET(baseURL+"/issues/1051", wrapper.Issue1051)
 	router.GET(baseURL+"/issues/127", wrapper.Issue127)
 	router.GET(baseURL+"/issues/185", wrapper.Issue185)
 	router.GET(baseURL+"/issues/209/$:str", wrapper.Issue209)
@@ -1448,30 +1579,32 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL
 // Base64 encoded, gzipped, json marshaled Swagger object
 var swaggerSpec = []string{
 
-	"H4sIAAAAAAAC/7RYT2/bOhL/KlNugb3YluO0aONbttstvMBrgyYPPdQ50OTYYiORKknZEQx994chZcuu",
-	"Jb/mpc0lksjh/ObfbzjeMmHywmjU3rHplhXc8hw92vB2663Sq5m+4T6ld4lOWFV4ZTSbsmtwYR0K7lPY",
-	"S7IBU7RMX9mAaZ4jmzLnacHi91JZlGzqbYkD5kSKOaejfVU025Resbqud4sByOtbz613X5RPP5b5Au0p",
-	"mrtUOYgiQDrBBRHYKJ8CBx3FBjtFZvENhWf1gF3r6q4q8IJNt+3bpMPcZgUsFhYdeQy4roAOHM31XEcE",
-	"qSkzCQsErkFpj3bJBW7ruSZd70rnTR7deheAbNnS2Jx7NmUiLLYQG18M2OPQ8EINhZG4Qj3ER2/50POV",
-	"i+KGTdmCW0Y++y9hE9yjvLGmQOurENX4rDBIaNzQ4qmFfzoET0Zos3lxgqMeMJOFYy+i6E7TLph92yfd",
-	"2491zzyUDiV4A9JEFFxL8Cn3Z5Bc/gwScmC7Z2iRu725H6MvQGnnkcsXB2e/+tWwn4ajPqyWr/ug7eGx",
-	"+45cfq/LfKY/Lb7N9LW1PARfeczdaRaseUb/UJc5nb9U1hFkh8JoeXD4viI71DUfeFBVD9gH1GiV+BQ3",
-	"tFXdSnwss4wvMrw5wnKMzATnRningW8Wr7XcnRVyev/ck4utL7f9i0879IcI7Z+7zzsNVx28XVrlq1vi",
-	"rWg9FwKdG3rzgJreF8gt2v/tSOL/X+6GkTEg7oSwczTXrGFMUhGF2gxMvS8iqSq9NB3kic6D4A4dLI2F",
-	"NbfKlA6Uc2X4VGoJZo0WvMpxBDcZcofApQQOfidLonNNlLgoV7BUjygjLK88OTFquUW7DtDWaF3UfjEa",
-	"j8YxuKh5odiUXY7Gows2CE0kuCVB7UqLQ1yjrXyq9Gqo3NDiEi1qEeO6Qt/TF1DLwijtAR+V8w6cCSUK",
-	"bfMDwTWxtrBI1QlKh2qea1egCDWtjacNhS01ymAXJS0nNTPJpux9APh+j2/mPrfoKCdcYbSLQZ6Mx/RP",
-	"GO1RB9C8KDIlwmnJt8AL24PueFwgvO1Y7KXFJZuyfyWtKUnTOJN9Z6sHO5nJT8pMSEZ0dKtzsifdrYM0",
-	"4t+AJTG3kovJm97Q/cEfEMipUGpXFoWxFJngtEcf+q4DafS/PRQWMS88tLvC6qgjTDPSS1qfGZJzjjjm",
-	"QTL38KzHPHvOUWR8knP7IM1GP/ugij8HDR0jccnLzP9G5/0ii3/MvLev+0mjKhBWJB8sgE2KGnatJ9nR",
-	"O7RlCdwi7PpFf9q9fd10B3T+P0ZWv8xpHX01WnuQ4wTv0AGT8VXycuu8rXv98C5F8eBALdvrfTRVosh4",
-	"64Ks6jZ4Mr5ipxgGR2PG127L2i3J0RhS3x+YcDlOtkueZT61plyl9akFn9FRw5HwgNXGWHl4Qy8shi5F",
-	"ZE8tjxwYZoeGOBqXdNh1Of4ZszrGoAOwTxqHjox+05+4dAFsgtNkLne7RCZW3CiBFE6fItDVL6wrTcNK",
-	"ZOi53qRKpM13pySCWdJyuOR1ZfYH9MEnjnD9RlI9udueVPSri2R7EWLQn9E3uxAdDIk0w4YxcT8kdoT8",
-	"VbyO/F2Ao/6zsT1n5OmgW9f3Z6v4qr94M4Xax8p1oSGC0sJYi8JnFT1npUQZbnwNJ0U3LIys6Moz1629",
-	"vZx21eOW7yXa6iDxjXlawv9jnmya0qEnPjXMHSxj51nx6kx1tdM1LBVmLZms0ANvuJAulTlq3+uw31sm",
-	"Hb8AdHgk/HZTiibg8sSu8HnNbUW1keEaM6IBaURJpgVcrKm+3QwTQn88vXy9pzgGAm5So7RZM45Mk6S5",
-	"7tMAMZKIRc6LEVfE8H8FAAD//5edhkCXEgAA",
+	"H4sIAAAAAAAC/7RYXW/buBL9Kyxvgftw/Z0GbfyW2+0WLrBJ0GTRhzoPtDi22EhDlaTsCIb++2JI2bIj",
+	"yW02bV5iiZzhmTPDM6S2PNJpphHQWT7d8kwYkYID459unVG4muGNcDE9S7CRUZlTGvmUXzLrx1kmXMz2",
+	"lrzHFQ3TW97jKFLgU24dDRj4nisDkk+dyaHHbRRDKsi1K7JqmsIVL8tyN+iBnN86YZz9olx8lacLME00",
+	"d7GyLJgwWpNZb8I2ysVMMAxmvd1CevENIsfLHr/E4q7IYMyn2/pp0hJuNcIMZAYsMcYEFowcDuY4x4Ag",
+	"1nki2QKYQKbQgVmKCLblHGmt97l1Og203nkgW77UJhWOT3nkB2uIFRc9/tjXIlP9SEtYAfbh0RnRd2Jl",
+	"g7nmU74QhhNnfxC2SDiQN0ZnYFzhsxp+K/AWCBsabEb4twXmKAjUm1cNHGWP68S7HQfT3Uq7ZHZNn7RP",
+	"P1575lhuQTKnmdQBhUDJXCzcCSRnP4OECKzn9A0Iuw/3KnDBFFoHQr468P3mV8N+Ho7ycLd83SdtD4/f",
+	"t9TyB8zTGV4vvs3w0hjhk68cpLZZBWuR0D/APCX/S2UsQbYQaZQHzvc7smW56oXwS5U9/hEQjIquw4R6",
+	"V9cWV3mSiEUCN0dYjpFpT26A10x8NXiJcufL1/T+d0ct1lxuuwef5/RJhva/2/21pes6d2BIB0jYLlFj",
+	"kerczhCDwB3TojpeH4ZEgrMC08CmZHN9KseV7tPLfiXSfuXPQE9yD+d6DzfM2v6ABz+rV8FtRl36GsuN",
+	"csUtqXWIQkQRWNt3+gGQnhcgDJg/d9L46ctdP+gkCzOZnzmYI6/6BC0RjOp9FzuXhVaicKlbWgZYxyJh",
+	"wbKlNmwtjNK5Zcra3L/KUTK9BsOcSmHAbhIQFpiQkgnmdrZkOkdqBIt8xZbqEWSA5ZSj0gmr3IJZe2hr",
+	"MDasPh6MBqNQ0oAiU3zKzwajwZj3fOv0tAwBbW6gD2swhYsVrvrK9g0swQBGoZpX4Dq6IaDMtELH4FFZ",
+	"Z5nVXphY3fJZJJB6VWSANIkp9Bo2R5tB5JUMtaMJmckRpI+Lik/QMjPJp/yDB/hhj29mP9foqDBsptGG",
+	"JE9GI/oXaXSAHrTIskRF3tvwm1fD7cGZ4LjQRd2n+WsDSz7l/xnWoQyr48Jw38/L3s5m8pM2E7KJWnr0",
+	"KdtGT2+RyvDX48NQW8Px6Hzcmbu/8sSpLAGWglTCny8sI9KEQvbp9vqqJQ0z8uu9vpDz5m49nL9GObA6",
+	"BZ/qwXr8vx87eBr55G134OIBGJUTy9HmWaYN1aSH/ugqHqTG/zqWGYA0c6ye5UcHncxM3r6UmFMlcNz3",
+	"npL2mCYvcUXBD1NhHqTe4IsdFeIlaMiNhKXIE/cbyftFET+tvHfn3XJZZMBWZO8jYJsYkO2OGsNdd2O1",
+	"IDFhgO3OB91l9+68Og2Adf/XsvhlpLWco0K0BzVO8A4JmIwuhq+31pmyk4f3MUQPlqllfZ0LoUqIElFT",
+	"kBTtAU9GF7yJoXd0rfzaHlk9ZXh07SzvD0I4Gw23S5EkLjY6X8VlM4LPYKnVSvYAxUYbeXgjywz4/kxt",
+	"jpo9EejvipVwVJS0xHU2+pmwWq69B2Cfdf09Cvptd+HSgb9KTlW5wu4KmVRxoyKgdLoYGB31/bhCupwG",
+	"hZ7jJlZRXL23SgLTSxr2h/q2yv4IznNiCddvFNXGXaaxo9+Mh9uxz0F3Rd/sUnTwUUDhKnwW2H8UaEn5",
+	"m3AQ+1GCw/onc3sqyOaHjbK8P7mLL7o3b6IAXdi51jdEpjDSxkDkkoJ+J7kE6c+6lSYFGhZaFnTYm2Md",
+	"b6emXXTQ8j0HUxwUvtbPK/h/rZNVUzpk4rpSbh8ZP62KFyd2V/01hS0VJLWYrMAxUWkhHadTQNdJ2O/d",
+	"Ji1ffFoY8d/q8qhKuGzE5V+vhSlobySwhoRkQOoop9A8Ll7tvt3tzaf++N729Z7y6AW4Ko3cJNVFbDoc",
+	"VhcdujoNJECWimwgFCn8PwEAAP//WUiKvIcUAAA=",
 }
 
 // GetSwagger returns the content of the embedded swagger specification file
diff --git a/internal/test/schemas/schemas.yaml b/internal/test/schemas/schemas.yaml
index 319faa7..2e094de 100644
--- a/internal/test/schemas/schemas.yaml
+++ b/internal/test/schemas/schemas.yaml
@@ -133,7 +133,20 @@ paths:
             application/json:
               schema:
                 $ref: "#/components/schemas/DeprecatedProperty"
-
+  /issues/1051:
+    get:
+      operationId: Issue1051
+      description: |
+        Multiple media types contain JSON
+      responses:
+        '200':
+          content:
+            application/vnd.something.v1+json:
+              schema:
+                type: object
+            application/json:
+              schema:
+                type: object
 components:
   schemas:
     GenericObject:
@@ -204,6 +217,19 @@ components:
           deprecated: true
           x-deprecated-reason: Use NewProp instead!
           description: It used to do this and that
+    OuterTypeWithAnonymousInner:
+      type: object
+      properties:
+        name:
+          type: string
+        inner:
+          type: object
+          x-go-type-name: InnerRenamedAnonymousObject
+          properties:
+            id:
+              type: integer
+          required: [ id ]
+      required: [ name, inner ]
   parameters:
     StringInPath:
       name: str
diff --git a/internal/test/strict-server/chi/server.gen.go b/internal/test/strict-server/chi/server.gen.go
index 236277c..b3904b5 100644
--- a/internal/test/strict-server/chi/server.gen.go
+++ b/internal/test/strict-server/chi/server.gen.go
@@ -892,9 +892,8 @@ type StrictServerInterface interface {
 	HeadersExample(ctx context.Context, request HeadersExampleRequestObject) (HeadersExampleResponseObject, error)
 }
 
-type StrictHandlerFunc func(ctx context.Context, w http.ResponseWriter, r *http.Request, args interface{}) (interface{}, error)
-
-type StrictMiddlewareFunc func(f StrictHandlerFunc, operationID string) StrictHandlerFunc
+type StrictHandlerFunc = runtime.StrictHttpHandlerFunc
+type StrictMiddlewareFunc = runtime.StrictHttpMiddlewareFunc
 
 type StrictHTTPServerOptions struct {
 	RequestErrorHandlerFunc  func(w http.ResponseWriter, r *http.Request, err error)
diff --git a/internal/test/strict-server/echo/server.gen.go b/internal/test/strict-server/echo/server.gen.go
index ebd374b..0912230 100644
--- a/internal/test/strict-server/echo/server.gen.go
+++ b/internal/test/strict-server/echo/server.gen.go
@@ -706,9 +706,8 @@ type StrictServerInterface interface {
 	HeadersExample(ctx context.Context, request HeadersExampleRequestObject) (HeadersExampleResponseObject, error)
 }
 
-type StrictHandlerFunc func(ctx echo.Context, args interface{}) (interface{}, error)
-
-type StrictMiddlewareFunc func(f StrictHandlerFunc, operationID string) StrictHandlerFunc
+type StrictHandlerFunc = runtime.StrictEchoHandlerFunc
+type StrictMiddlewareFunc = runtime.StrictEchoMiddlewareFunc
 
 func NewStrictHandler(ssi StrictServerInterface, middlewares []StrictMiddlewareFunc) ServerInterface {
 	return &strictHandler{ssi: ssi, middlewares: middlewares}
diff --git a/internal/test/strict-server/fiber/server.cfg.yaml b/internal/test/strict-server/fiber/server.cfg.yaml
new file mode 100644
index 0000000..3a64712
--- /dev/null
+++ b/internal/test/strict-server/fiber/server.cfg.yaml
@@ -0,0 +1,6 @@
+package: api
+generate:
+  fiber-server: true
+  strict-server: true
+  embedded-spec: true
+output: server.gen.go
diff --git a/internal/test/strict-server/fiber/server.gen.go b/internal/test/strict-server/fiber/server.gen.go
new file mode 100644
index 0000000..8467150
--- /dev/null
+++ b/internal/test/strict-server/fiber/server.gen.go
@@ -0,0 +1,1105 @@
+// Package api provides primitives to interact with the openapi HTTP API.
+//
+// Code generated by github.com/deepmap/oapi-codegen version (devel) DO NOT EDIT.
+package api
+
+import (
+	"bytes"
+	"compress/gzip"
+	"context"
+	"encoding/base64"
+	"fmt"
+	"io"
+	"mime/multipart"
+	"net/http"
+	"net/url"
+	"path"
+	"strings"
+
+	"github.com/deepmap/oapi-codegen/pkg/runtime"
+	"github.com/getkin/kin-openapi/openapi3"
+	"github.com/gofiber/fiber/v2"
+)
+
+// ServerInterface represents all server handlers.
+type ServerInterface interface {
+
+	// (POST /json)
+	JSONExample(c *fiber.Ctx) error
+
+	// (POST /multipart)
+	MultipartExample(c *fiber.Ctx) error
+
+	// (POST /multiple)
+	MultipleRequestAndResponseTypes(c *fiber.Ctx) error
+
+	// (GET /reserved-go-keyword-parameters/{type})
+	ReservedGoKeywordParameters(c *fiber.Ctx, pType string) error
+
+	// (POST /reusable-responses)
+	ReusableResponses(c *fiber.Ctx) error
+
+	// (POST /text)
+	TextExample(c *fiber.Ctx) error
+
+	// (POST /unknown)
+	UnknownExample(c *fiber.Ctx) error
+
+	// (POST /unspecified-content-type)
+	UnspecifiedContentType(c *fiber.Ctx) error
+
+	// (POST /urlencoded)
+	URLEncodedExample(c *fiber.Ctx) error
+
+	// (POST /with-headers)
+	HeadersExample(c *fiber.Ctx, params HeadersExampleParams) error
+}
+
+// ServerInterfaceWrapper converts contexts to parameters.
+type ServerInterfaceWrapper struct {
+	Handler ServerInterface
+}
+
+type MiddlewareFunc fiber.Handler
+
+// JSONExample operation middleware
+func (siw *ServerInterfaceWrapper) JSONExample(c *fiber.Ctx) error {
+
+	return siw.Handler.JSONExample(c)
+}
+
+// MultipartExample operation middleware
+func (siw *ServerInterfaceWrapper) MultipartExample(c *fiber.Ctx) error {
+
+	return siw.Handler.MultipartExample(c)
+}
+
+// MultipleRequestAndResponseTypes operation middleware
+func (siw *ServerInterfaceWrapper) MultipleRequestAndResponseTypes(c *fiber.Ctx) error {
+
+	return siw.Handler.MultipleRequestAndResponseTypes(c)
+}
+
+// ReservedGoKeywordParameters operation middleware
+func (siw *ServerInterfaceWrapper) ReservedGoKeywordParameters(c *fiber.Ctx) error {
+
+	var err error
+
+	// ------------- Path parameter "type" -------------
+	var pType string
+
+	err = runtime.BindStyledParameter("simple", false, "type", c.Params("type"), &pType)
+	if err != nil {
+		return fiber.NewError(fiber.StatusBadRequest, fmt.Errorf("Invalid format for parameter type: %w", err).Error())
+	}
+
+	return siw.Handler.ReservedGoKeywordParameters(c, pType)
+}
+
+// ReusableResponses operation middleware
+func (siw *ServerInterfaceWrapper) ReusableResponses(c *fiber.Ctx) error {
+
+	return siw.Handler.ReusableResponses(c)
+}
+
+// TextExample operation middleware
+func (siw *ServerInterfaceWrapper) TextExample(c *fiber.Ctx) error {
+
+	return siw.Handler.TextExample(c)
+}
+
+// UnknownExample operation middleware
+func (siw *ServerInterfaceWrapper) UnknownExample(c *fiber.Ctx) error {
+
+	return siw.Handler.UnknownExample(c)
+}
+
+// UnspecifiedContentType operation middleware
+func (siw *ServerInterfaceWrapper) UnspecifiedContentType(c *fiber.Ctx) error {
+
+	return siw.Handler.UnspecifiedContentType(c)
+}
+
+// URLEncodedExample operation middleware
+func (siw *ServerInterfaceWrapper) URLEncodedExample(c *fiber.Ctx) error {
+
+	return siw.Handler.URLEncodedExample(c)
+}
+
+// HeadersExample operation middleware
+func (siw *ServerInterfaceWrapper) HeadersExample(c *fiber.Ctx) error {
+
+	var err error
+
+	// Parameter object where we will unmarshal all parameters from the context
+	var params HeadersExampleParams
+
+	headers := c.GetReqHeaders()
+
+	// ------------- Required header parameter "header1" -------------
+	if value, found := headers[http.CanonicalHeaderKey("header1")]; found {
+		var Header1 string
+
+		err = runtime.BindStyledParameterWithLocation("simple", false, "header1", runtime.ParamLocationHeader, value, &Header1)
+		if err != nil {
+			return fiber.NewError(fiber.StatusBadRequest, fmt.Errorf("Invalid format for parameter header1: %w", err).Error())
+		}
+
+		params.Header1 = Header1
+
+	} else {
+		err = fmt.Errorf("Header parameter header1 is required, but not found: %w", err)
+		return fiber.NewError(fiber.StatusBadRequest, err.Error())
+	}
+
+	// ------------- Optional header parameter "header2" -------------
+	if value, found := headers[http.CanonicalHeaderKey("header2")]; found {
+		var Header2 int
+
+		err = runtime.BindStyledParameterWithLocation("simple", false, "header2", runtime.ParamLocationHeader, value, &Header2)
+		if err != nil {
+			return fiber.NewError(fiber.StatusBadRequest, fmt.Errorf("Invalid format for parameter header2: %w", err).Error())
+		}
+
+		params.Header2 = &Header2
+
+	}
+
+	return siw.Handler.HeadersExample(c, params)
+}
+
+// FiberServerOptions provides options for the Fiber server.
+type FiberServerOptions struct {
+	BaseURL     string
+	Middlewares []MiddlewareFunc
+}
+
+// RegisterHandlers creates http.Handler with routing matching OpenAPI spec.
+func RegisterHandlers(router fiber.Router, si ServerInterface) {
+	RegisterHandlersWithOptions(router, si, FiberServerOptions{})
+}
+
+// RegisterHandlersWithOptions creates http.Handler with additional options
+func RegisterHandlersWithOptions(router fiber.Router, si ServerInterface, options FiberServerOptions) {
+	wrapper := ServerInterfaceWrapper{
+		Handler: si,
+	}
+
+	for _, m := range options.Middlewares {
+		router.Use(m)
+	}
+
+	router.Post(options.BaseURL+"/json", wrapper.JSONExample)
+
+	router.Post(options.BaseURL+"/multipart", wrapper.MultipartExample)
+
+	router.Post(options.BaseURL+"/multiple", wrapper.MultipleRequestAndResponseTypes)
+
+	router.Get(options.BaseURL+"/reserved-go-keyword-parameters/:type", wrapper.ReservedGoKeywordParameters)
+
+	router.Post(options.BaseURL+"/reusable-responses", wrapper.ReusableResponses)
+
+	router.Post(options.BaseURL+"/text", wrapper.TextExample)
+
+	router.Post(options.BaseURL+"/unknown", wrapper.UnknownExample)
+
+	router.Post(options.BaseURL+"/unspecified-content-type", wrapper.UnspecifiedContentType)
+
+	router.Post(options.BaseURL+"/urlencoded", wrapper.URLEncodedExample)
+
+	router.Post(options.BaseURL+"/with-headers", wrapper.HeadersExample)
+
+}
+
+type BadrequestResponse struct {
+}
+
+type ReusableresponseResponseHeaders struct {
+	Header1 string
+	Header2 int
+}
+type ReusableresponseJSONResponse struct {
+	Body Example
+
+	Headers ReusableresponseResponseHeaders
+}
+
+type JSONExampleRequestObject struct {
+	Body *JSONExampleJSONRequestBody
+}
+
+type JSONExampleResponseObject interface {
+	VisitJSONExampleResponse(ctx *fiber.Ctx) error
+}
+
+type JSONExample200JSONResponse Example
+
+func (response JSONExample200JSONResponse) VisitJSONExampleResponse(ctx *fiber.Ctx) error {
+	ctx.Response().Header.Set("Content-Type", "application/json")
+	ctx.Status(200)
+
+	return ctx.JSON(&response)
+}
+
+type JSONExample400Response = BadrequestResponse
+
+func (response JSONExample400Response) VisitJSONExampleResponse(ctx *fiber.Ctx) error {
+	ctx.Status(400)
+	return nil
+}
+
+type JSONExampledefaultResponse struct {
+	StatusCode int
+}
+
+func (response JSONExampledefaultResponse) VisitJSONExampleResponse(ctx *fiber.Ctx) error {
+	ctx.Status(response.StatusCode)
+	return nil
+}
+
+type MultipartExampleRequestObject struct {
+	Body *multipart.Reader
+}
+
+type MultipartExampleResponseObject interface {
+	VisitMultipartExampleResponse(ctx *fiber.Ctx) error
+}
+
+type MultipartExample200MultipartResponse func(writer *multipart.Writer) error
+
+func (response MultipartExample200MultipartResponse) VisitMultipartExampleResponse(ctx *fiber.Ctx) error {
+	writer := multipart.NewWriter(ctx.Response().BodyWriter())
+	ctx.Response().Header.Set("Content-Type", writer.FormDataContentType())
+	ctx.Status(200)
+
+	defer writer.Close()
+	return response(writer)
+}
+
+type MultipartExample400Response = BadrequestResponse
+
+func (response MultipartExample400Response) VisitMultipartExampleResponse(ctx *fiber.Ctx) error {
+	ctx.Status(400)
+	return nil
+}
+
+type MultipartExampledefaultResponse struct {
+	StatusCode int
+}
+
+func (response MultipartExampledefaultResponse) VisitMultipartExampleResponse(ctx *fiber.Ctx) error {
+	ctx.Status(response.StatusCode)
+	return nil
+}
+
+type MultipleRequestAndResponseTypesRequestObject struct {
+	JSONBody      *MultipleRequestAndResponseTypesJSONRequestBody
+	FormdataBody  *MultipleRequestAndResponseTypesFormdataRequestBody
+	Body          io.Reader
+	MultipartBody *multipart.Reader
+	TextBody      *MultipleRequestAndResponseTypesTextRequestBody
+}
+
+type MultipleRequestAndResponseTypesResponseObject interface {
+	VisitMultipleRequestAndResponseTypesResponse(ctx *fiber.Ctx) error
+}
+
+type MultipleRequestAndResponseTypes200JSONResponse Example
+
+func (response MultipleRequestAndResponseTypes200JSONResponse) VisitMultipleRequestAndResponseTypesResponse(ctx *fiber.Ctx) error {
+	ctx.Response().Header.Set("Content-Type", "application/json")
+	ctx.Status(200)
+
+	return ctx.JSON(&response)
+}
+
+type MultipleRequestAndResponseTypes200FormdataResponse Example
+
+func (response MultipleRequestAndResponseTypes200FormdataResponse) VisitMultipleRequestAndResponseTypesResponse(ctx *fiber.Ctx) error {
+	ctx.Response().Header.Set("Content-Type", "application/x-www-form-urlencoded")
+	ctx.Status(200)
+
+	if form, err := runtime.MarshalForm(response, nil); err != nil {
+		return err
+	} else {
+		_, err := ctx.WriteString(form.Encode())
+		return err
+	}
+}
+
+type MultipleRequestAndResponseTypes200ImagepngResponse struct {
+	Body          io.Reader
+	ContentLength int64
+}
+
+func (response MultipleRequestAndResponseTypes200ImagepngResponse) VisitMultipleRequestAndResponseTypesResponse(ctx *fiber.Ctx) error {
+	ctx.Response().Header.Set("Content-Type", "image/png")
+	if response.ContentLength != 0 {
+		ctx.Response().Header.Set("Content-Length", fmt.Sprint(response.ContentLength))
+	}
+	ctx.Status(200)
+
+	if closer, ok := response.Body.(io.ReadCloser); ok {
+		defer closer.Close()
+	}
+	_, err := io.Copy(ctx.Response().BodyWriter(), response.Body)
+	return err
+}
+
+type MultipleRequestAndResponseTypes200MultipartResponse func(writer *multipart.Writer) error
+
+func (response MultipleRequestAndResponseTypes200MultipartResponse) VisitMultipleRequestAndResponseTypesResponse(ctx *fiber.Ctx) error {
+	writer := multipart.NewWriter(ctx.Response().BodyWriter())
+	ctx.Response().Header.Set("Content-Type", writer.FormDataContentType())
+	ctx.Status(200)
+
+	defer writer.Close()
+	return response(writer)
+}
+
+type MultipleRequestAndResponseTypes200TextResponse string
+
+func (response MultipleRequestAndResponseTypes200TextResponse) VisitMultipleRequestAndResponseTypesResponse(ctx *fiber.Ctx) error {
+	ctx.Response().Header.Set("Content-Type", "text/plain")
+	ctx.Status(200)
+
+	_, err := ctx.WriteString(string(response))
+	return err
+}
+
+type MultipleRequestAndResponseTypes400Response = BadrequestResponse
+
+func (response MultipleRequestAndResponseTypes400Response) VisitMultipleRequestAndResponseTypesResponse(ctx *fiber.Ctx) error {
+	ctx.Status(400)
+	return nil
+}
+
+type ReservedGoKeywordParametersRequestObject struct {
+	Type string `json:"type"`
+}
+
+type ReservedGoKeywordParametersResponseObject interface {
+	VisitReservedGoKeywordParametersResponse(ctx *fiber.Ctx) error
+}
+
+type ReservedGoKeywordParameters200TextResponse string
+
+func (response ReservedGoKeywordParameters200TextResponse) VisitReservedGoKeywordParametersResponse(ctx *fiber.Ctx) error {
+	ctx.Response().Header.Set("Content-Type", "text/plain")
+	ctx.Status(200)
+
+	_, err := ctx.WriteString(string(response))
+	return err
+}
+
+type ReusableResponsesRequestObject struct {
+	Body *ReusableResponsesJSONRequestBody
+}
+
+type ReusableResponsesResponseObject interface {
+	VisitReusableResponsesResponse(ctx *fiber.Ctx) error
+}
+
+type ReusableResponses200JSONResponse struct{ ReusableresponseJSONResponse }
+
+func (response ReusableResponses200JSONResponse) VisitReusableResponsesResponse(ctx *fiber.Ctx) error {
+	ctx.Response().Header.Set("header1", fmt.Sprint(response.Headers.Header1))
+	ctx.Response().Header.Set("header2", fmt.Sprint(response.Headers.Header2))
+	ctx.Response().Header.Set("Content-Type", "application/json")
+	ctx.Status(200)
+
+	return ctx.JSON(&response.Body)
+}
+
+type ReusableResponses400Response = BadrequestResponse
+
+func (response ReusableResponses400Response) VisitReusableResponsesResponse(ctx *fiber.Ctx) error {
+	ctx.Status(400)
+	return nil
+}
+
+type ReusableResponsesdefaultResponse struct {
+	StatusCode int
+}
+
+func (response ReusableResponsesdefaultResponse) VisitReusableResponsesResponse(ctx *fiber.Ctx) error {
+	ctx.Status(response.StatusCode)
+	return nil
+}
+
+type TextExampleRequestObject struct {
+	Body *TextExampleTextRequestBody
+}
+
+type TextExampleResponseObject interface {
+	VisitTextExampleResponse(ctx *fiber.Ctx) error
+}
+
+type TextExample200TextResponse string
+
+func (response TextExample200TextResponse) VisitTextExampleResponse(ctx *fiber.Ctx) error {
+	ctx.Response().Header.Set("Content-Type", "text/plain")
+	ctx.Status(200)
+
+	_, err := ctx.WriteString(string(response))
+	return err
+}
+
+type TextExample400Response = BadrequestResponse
+
+func (response TextExample400Response) VisitTextExampleResponse(ctx *fiber.Ctx) error {
+	ctx.Status(400)
+	return nil
+}
+
+type TextExampledefaultResponse struct {
+	StatusCode int
+}
+
+func (response TextExampledefaultResponse) VisitTextExampleResponse(ctx *fiber.Ctx) error {
+	ctx.Status(response.StatusCode)
+	return nil
+}
+
+type UnknownExampleRequestObject struct {
+	Body io.Reader
+}
+
+type UnknownExampleResponseObject interface {
+	VisitUnknownExampleResponse(ctx *fiber.Ctx) error
+}
+
+type UnknownExample200Videomp4Response struct {
+	Body          io.Reader
+	ContentLength int64
+}
+
+func (response UnknownExample200Videomp4Response) VisitUnknownExampleResponse(ctx *fiber.Ctx) error {
+	ctx.Response().Header.Set("Content-Type", "video/mp4")
+	if response.ContentLength != 0 {
+		ctx.Response().Header.Set("Content-Length", fmt.Sprint(response.ContentLength))
+	}
+	ctx.Status(200)
+
+	if closer, ok := response.Body.(io.ReadCloser); ok {
+		defer closer.Close()
+	}
+	_, err := io.Copy(ctx.Response().BodyWriter(), response.Body)
+	return err
+}
+
+type UnknownExample400Response = BadrequestResponse
+
+func (response UnknownExample400Response) VisitUnknownExampleResponse(ctx *fiber.Ctx) error {
+	ctx.Status(400)
+	return nil
+}
+
+type UnknownExampledefaultResponse struct {
+	StatusCode int
+}
+
+func (response UnknownExampledefaultResponse) VisitUnknownExampleResponse(ctx *fiber.Ctx) error {
+	ctx.Status(response.StatusCode)
+	return nil
+}
+
+type UnspecifiedContentTypeRequestObject struct {
+	ContentType string
+	Body        io.Reader
+}
+
+type UnspecifiedContentTypeResponseObject interface {
+	VisitUnspecifiedContentTypeResponse(ctx *fiber.Ctx) error
+}
+
+type UnspecifiedContentType200VideoResponse struct {
+	Body          io.Reader
+	ContentType   string
+	ContentLength int64
+}
+
+func (response UnspecifiedContentType200VideoResponse) VisitUnspecifiedContentTypeResponse(ctx *fiber.Ctx) error {
+	ctx.Response().Header.Set("Content-Type", response.ContentType)
+	if response.ContentLength != 0 {
+		ctx.Response().Header.Set("Content-Length", fmt.Sprint(response.ContentLength))
+	}
+	ctx.Status(200)
+
+	if closer, ok := response.Body.(io.ReadCloser); ok {
+		defer closer.Close()
+	}
+	_, err := io.Copy(ctx.Response().BodyWriter(), response.Body)
+	return err
+}
+
+type UnspecifiedContentType400Response = BadrequestResponse
+
+func (response UnspecifiedContentType400Response) VisitUnspecifiedContentTypeResponse(ctx *fiber.Ctx) error {
+	ctx.Status(400)
+	return nil
+}
+
+type UnspecifiedContentType401Response struct {
+}
+
+func (response UnspecifiedContentType401Response) VisitUnspecifiedContentTypeResponse(ctx *fiber.Ctx) error {
+	ctx.Status(401)
+	return nil
+}
+
+type UnspecifiedContentType403Response struct {
+}
+
+func (response UnspecifiedContentType403Response) VisitUnspecifiedContentTypeResponse(ctx *fiber.Ctx) error {
+	ctx.Status(403)
+	return nil
+}
+
+type UnspecifiedContentTypedefaultResponse struct {
+	StatusCode int
+}
+
+func (response UnspecifiedContentTypedefaultResponse) VisitUnspecifiedContentTypeResponse(ctx *fiber.Ctx) error {
+	ctx.Status(response.StatusCode)
+	return nil
+}
+
+type URLEncodedExampleRequestObject struct {
+	Body *URLEncodedExampleFormdataRequestBody
+}
+
+type URLEncodedExampleResponseObject interface {
+	VisitURLEncodedExampleResponse(ctx *fiber.Ctx) error
+}
+
+type URLEncodedExample200FormdataResponse Example
+
+func (response URLEncodedExample200FormdataResponse) VisitURLEncodedExampleResponse(ctx *fiber.Ctx) error {
+	ctx.Response().Header.Set("Content-Type", "application/x-www-form-urlencoded")
+	ctx.Status(200)
+
+	if form, err := runtime.MarshalForm(response, nil); err != nil {
+		return err
+	} else {
+		_, err := ctx.WriteString(form.Encode())
+		return err
+	}
+}
+
+type URLEncodedExample400Response = BadrequestResponse
+
+func (response URLEncodedExample400Response) VisitURLEncodedExampleResponse(ctx *fiber.Ctx) error {
+	ctx.Status(400)
+	return nil
+}
+
+type URLEncodedExampledefaultResponse struct {
+	StatusCode int
+}
+
+func (response URLEncodedExampledefaultResponse) VisitURLEncodedExampleResponse(ctx *fiber.Ctx) error {
+	ctx.Status(response.StatusCode)
+	return nil
+}
+
+type HeadersExampleRequestObject struct {
+	Params HeadersExampleParams
+	Body   *HeadersExampleJSONRequestBody
+}
+
+type HeadersExampleResponseObject interface {
+	VisitHeadersExampleResponse(ctx *fiber.Ctx) error
+}
+
+type HeadersExample200ResponseHeaders struct {
+	Header1 string
+	Header2 int
+}
+
+type HeadersExample200JSONResponse struct {
+	Body    Example
+	Headers HeadersExample200ResponseHeaders
+}
+
+func (response HeadersExample200JSONResponse) VisitHeadersExampleResponse(ctx *fiber.Ctx) error {
+	ctx.Response().Header.Set("header1", fmt.Sprint(response.Headers.Header1))
+	ctx.Response().Header.Set("header2", fmt.Sprint(response.Headers.Header2))
+	ctx.Response().Header.Set("Content-Type", "application/json")
+	ctx.Status(200)
+
+	return ctx.JSON(&response.Body)
+}
+
+type HeadersExample400Response = BadrequestResponse
+
+func (response HeadersExample400Response) VisitHeadersExampleResponse(ctx *fiber.Ctx) error {
+	ctx.Status(400)
+	return nil
+}
+
+type HeadersExampledefaultResponse struct {
+	StatusCode int
+}
+
+func (response HeadersExampledefaultResponse) VisitHeadersExampleResponse(ctx *fiber.Ctx) error {
+	ctx.Status(response.StatusCode)
+	return nil
+}
+
+// StrictServerInterface represents all server handlers.
+type StrictServerInterface interface {
+
+	// (POST /json)
+	JSONExample(ctx context.Context, request JSONExampleRequestObject) (JSONExampleResponseObject, error)
+
+	// (POST /multipart)
+	MultipartExample(ctx context.Context, request MultipartExampleRequestObject) (MultipartExampleResponseObject, error)
+
+	// (POST /multiple)
+	MultipleRequestAndResponseTypes(ctx context.Context, request MultipleRequestAndResponseTypesRequestObject) (MultipleRequestAndResponseTypesResponseObject, error)
+
+	// (GET /reserved-go-keyword-parameters/{type})
+	ReservedGoKeywordParameters(ctx context.Context, request ReservedGoKeywordParametersRequestObject) (ReservedGoKeywordParametersResponseObject, error)
+
+	// (POST /reusable-responses)
+	ReusableResponses(ctx context.Context, request ReusableResponsesRequestObject) (ReusableResponsesResponseObject, error)
+
+	// (POST /text)
+	TextExample(ctx context.Context, request TextExampleRequestObject) (TextExampleResponseObject, error)
+
+	// (POST /unknown)
+	UnknownExample(ctx context.Context, request UnknownExampleRequestObject) (UnknownExampleResponseObject, error)
+
+	// (POST /unspecified-content-type)
+	UnspecifiedContentType(ctx context.Context, request UnspecifiedContentTypeRequestObject) (UnspecifiedContentTypeResponseObject, error)
+
+	// (POST /urlencoded)
+	URLEncodedExample(ctx context.Context, request URLEncodedExampleRequestObject) (URLEncodedExampleResponseObject, error)
+
+	// (POST /with-headers)
+	HeadersExample(ctx context.Context, request HeadersExampleRequestObject) (HeadersExampleResponseObject, error)
+}
+
+type StrictHandlerFunc func(ctx *fiber.Ctx, args interface{}) (interface{}, error)
+
+type StrictMiddlewareFunc func(f StrictHandlerFunc, operationID string) StrictHandlerFunc
+
+func NewStrictHandler(ssi StrictServerInterface, middlewares []StrictMiddlewareFunc) ServerInterface {
+	return &strictHandler{ssi: ssi, middlewares: middlewares}
+}
+
+type strictHandler struct {
+	ssi         StrictServerInterface
+	middlewares []StrictMiddlewareFunc
+}
+
+// JSONExample operation middleware
+func (sh *strictHandler) JSONExample(ctx *fiber.Ctx) error {
+	var request JSONExampleRequestObject
+
+	var body JSONExampleJSONRequestBody
+	if err := ctx.BodyParser(&body); err != nil {
+		return fiber.NewError(fiber.StatusBadRequest, err.Error())
+	}
+	request.Body = &body
+
+	handler := func(ctx *fiber.Ctx, request interface{}) (interface{}, error) {
+		return sh.ssi.JSONExample(ctx.UserContext(), request.(JSONExampleRequestObject))
+	}
+	for _, middleware := range sh.middlewares {
+		handler = middleware(handler, "JSONExample")
+	}
+
+	response, err := handler(ctx, request)
+
+	if err != nil {
+		return fiber.NewError(fiber.StatusBadRequest, err.Error())
+	} else if validResponse, ok := response.(JSONExampleResponseObject); ok {
+		if err := validResponse.VisitJSONExampleResponse(ctx); err != nil {
+			return fiber.NewError(fiber.StatusBadRequest, err.Error())
+		}
+	} else if response != nil {
+		return fmt.Errorf("Unexpected response type: %T", response)
+	}
+	return nil
+}
+
+// MultipartExample operation middleware
+func (sh *strictHandler) MultipartExample(ctx *fiber.Ctx) error {
+	var request MultipartExampleRequestObject
+
+	request.Body = multipart.NewReader(bytes.NewReader(ctx.Request().Body()), string(ctx.Request().Header.MultipartFormBoundary()))
+
+	handler := func(ctx *fiber.Ctx, request interface{}) (interface{}, error) {
+		return sh.ssi.MultipartExample(ctx.UserContext(), request.(MultipartExampleRequestObject))
+	}
+	for _, middleware := range sh.middlewares {
+		handler = middleware(handler, "MultipartExample")
+	}
+
+	response, err := handler(ctx, request)
+
+	if err != nil {
+		return fiber.NewError(fiber.StatusBadRequest, err.Error())
+	} else if validResponse, ok := response.(MultipartExampleResponseObject); ok {
+		if err := validResponse.VisitMultipartExampleResponse(ctx); err != nil {
+			return fiber.NewError(fiber.StatusBadRequest, err.Error())
+		}
+	} else if response != nil {
+		return fmt.Errorf("Unexpected response type: %T", response)
+	}
+	return nil
+}
+
+// MultipleRequestAndResponseTypes operation middleware
+func (sh *strictHandler) MultipleRequestAndResponseTypes(ctx *fiber.Ctx) error {
+	var request MultipleRequestAndResponseTypesRequestObject
+
+	if strings.HasPrefix(string(ctx.Request().Header.ContentType()), "application/json") {
+		var body MultipleRequestAndResponseTypesJSONRequestBody
+		if err := ctx.BodyParser(&body); err != nil {
+			return fiber.NewError(fiber.StatusBadRequest, err.Error())
+		}
+		request.JSONBody = &body
+	}
+	if strings.HasPrefix(string(ctx.Request().Header.ContentType()), "application/x-www-form-urlencoded") {
+		var body MultipleRequestAndResponseTypesFormdataRequestBody
+		if err := ctx.BodyParser(&body); err != nil {
+			return fiber.NewError(fiber.StatusBadRequest, err.Error())
+		}
+		request.FormdataBody = &body
+	}
+	if strings.HasPrefix(string(ctx.Request().Header.ContentType()), "image/png") {
+		request.Body = bytes.NewReader(ctx.Request().Body())
+	}
+	if strings.HasPrefix(string(ctx.Request().Header.ContentType()), "multipart/form-data") {
+		request.MultipartBody = multipart.NewReader(bytes.NewReader(ctx.Request().Body()), string(ctx.Request().Header.MultipartFormBoundary()))
+	}
+	if strings.HasPrefix(string(ctx.Request().Header.ContentType()), "text/plain") {
+		data := ctx.Request().Body()
+		body := MultipleRequestAndResponseTypesTextRequestBody(data)
+		request.TextBody = &body
+	}
+
+	handler := func(ctx *fiber.Ctx, request interface{}) (interface{}, error) {
+		return sh.ssi.MultipleRequestAndResponseTypes(ctx.UserContext(), request.(MultipleRequestAndResponseTypesRequestObject))
+	}
+	for _, middleware := range sh.middlewares {
+		handler = middleware(handler, "MultipleRequestAndResponseTypes")
+	}
+
+	response, err := handler(ctx, request)
+
+	if err != nil {
+		return fiber.NewError(fiber.StatusBadRequest, err.Error())
+	} else if validResponse, ok := response.(MultipleRequestAndResponseTypesResponseObject); ok {
+		if err := validResponse.VisitMultipleRequestAndResponseTypesResponse(ctx); err != nil {
+			return fiber.NewError(fiber.StatusBadRequest, err.Error())
+		}
+	} else if response != nil {
+		return fmt.Errorf("Unexpected response type: %T", response)
+	}
+	return nil
+}
+
+// ReservedGoKeywordParameters operation middleware
+func (sh *strictHandler) ReservedGoKeywordParameters(ctx *fiber.Ctx, pType string) error {
+	var request ReservedGoKeywordParametersRequestObject
+
+	request.Type = pType
+
+	handler := func(ctx *fiber.Ctx, request interface{}) (interface{}, error) {
+		return sh.ssi.ReservedGoKeywordParameters(ctx.UserContext(), request.(ReservedGoKeywordParametersRequestObject))
+	}
+	for _, middleware := range sh.middlewares {
+		handler = middleware(handler, "ReservedGoKeywordParameters")
+	}
+
+	response, err := handler(ctx, request)
+
+	if err != nil {
+		return fiber.NewError(fiber.StatusBadRequest, err.Error())
+	} else if validResponse, ok := response.(ReservedGoKeywordParametersResponseObject); ok {
+		if err := validResponse.VisitReservedGoKeywordParametersResponse(ctx); err != nil {
+			return fiber.NewError(fiber.StatusBadRequest, err.Error())
+		}
+	} else if response != nil {
+		return fmt.Errorf("Unexpected response type: %T", response)
+	}
+	return nil
+}
+
+// ReusableResponses operation middleware
+func (sh *strictHandler) ReusableResponses(ctx *fiber.Ctx) error {
+	var request ReusableResponsesRequestObject
+
+	var body ReusableResponsesJSONRequestBody
+	if err := ctx.BodyParser(&body); err != nil {
+		return fiber.NewError(fiber.StatusBadRequest, err.Error())
+	}
+	request.Body = &body
+
+	handler := func(ctx *fiber.Ctx, request interface{}) (interface{}, error) {
+		return sh.ssi.ReusableResponses(ctx.UserContext(), request.(ReusableResponsesRequestObject))
+	}
+	for _, middleware := range sh.middlewares {
+		handler = middleware(handler, "ReusableResponses")
+	}
+
+	response, err := handler(ctx, request)
+
+	if err != nil {
+		return fiber.NewError(fiber.StatusBadRequest, err.Error())
+	} else if validResponse, ok := response.(ReusableResponsesResponseObject); ok {
+		if err := validResponse.VisitReusableResponsesResponse(ctx); err != nil {
+			return fiber.NewError(fiber.StatusBadRequest, err.Error())
+		}
+	} else if response != nil {
+		return fmt.Errorf("Unexpected response type: %T", response)
+	}
+	return nil
+}
+
+// TextExample operation middleware
+func (sh *strictHandler) TextExample(ctx *fiber.Ctx) error {
+	var request TextExampleRequestObject
+
+	data := ctx.Request().Body()
+	body := TextExampleTextRequestBody(data)
+	request.Body = &body
+
+	handler := func(ctx *fiber.Ctx, request interface{}) (interface{}, error) {
+		return sh.ssi.TextExample(ctx.UserContext(), request.(TextExampleRequestObject))
+	}
+	for _, middleware := range sh.middlewares {
+		handler = middleware(handler, "TextExample")
+	}
+
+	response, err := handler(ctx, request)
+
+	if err != nil {
+		return fiber.NewError(fiber.StatusBadRequest, err.Error())
+	} else if validResponse, ok := response.(TextExampleResponseObject); ok {
+		if err := validResponse.VisitTextExampleResponse(ctx); err != nil {
+			return fiber.NewError(fiber.StatusBadRequest, err.Error())
+		}
+	} else if response != nil {
+		return fmt.Errorf("Unexpected response type: %T", response)
+	}
+	return nil
+}
+
+// UnknownExample operation middleware
+func (sh *strictHandler) UnknownExample(ctx *fiber.Ctx) error {
+	var request UnknownExampleRequestObject
+
+	request.Body = bytes.NewReader(ctx.Request().Body())
+
+	handler := func(ctx *fiber.Ctx, request interface{}) (interface{}, error) {
+		return sh.ssi.UnknownExample(ctx.UserContext(), request.(UnknownExampleRequestObject))
+	}
+	for _, middleware := range sh.middlewares {
+		handler = middleware(handler, "UnknownExample")
+	}
+
+	response, err := handler(ctx, request)
+
+	if err != nil {
+		return fiber.NewError(fiber.StatusBadRequest, err.Error())
+	} else if validResponse, ok := response.(UnknownExampleResponseObject); ok {
+		if err := validResponse.VisitUnknownExampleResponse(ctx); err != nil {
+			return fiber.NewError(fiber.StatusBadRequest, err.Error())
+		}
+	} else if response != nil {
+		return fmt.Errorf("Unexpected response type: %T", response)
+	}
+	return nil
+}
+
+// UnspecifiedContentType operation middleware
+func (sh *strictHandler) UnspecifiedContentType(ctx *fiber.Ctx) error {
+	var request UnspecifiedContentTypeRequestObject
+
+	request.ContentType = string(ctx.Request().Header.ContentType())
+
+	request.Body = bytes.NewReader(ctx.Request().Body())
+
+	handler := func(ctx *fiber.Ctx, request interface{}) (interface{}, error) {
+		return sh.ssi.UnspecifiedContentType(ctx.UserContext(), request.(UnspecifiedContentTypeRequestObject))
+	}
+	for _, middleware := range sh.middlewares {
+		handler = middleware(handler, "UnspecifiedContentType")
+	}
+
+	response, err := handler(ctx, request)
+
+	if err != nil {
+		return fiber.NewError(fiber.StatusBadRequest, err.Error())
+	} else if validResponse, ok := response.(UnspecifiedContentTypeResponseObject); ok {
+		if err := validResponse.VisitUnspecifiedContentTypeResponse(ctx); err != nil {
+			return fiber.NewError(fiber.StatusBadRequest, err.Error())
+		}
+	} else if response != nil {
+		return fmt.Errorf("Unexpected response type: %T", response)
+	}
+	return nil
+}
+
+// URLEncodedExample operation middleware
+func (sh *strictHandler) URLEncodedExample(ctx *fiber.Ctx) error {
+	var request URLEncodedExampleRequestObject
+
+	var body URLEncodedExampleFormdataRequestBody
+	if err := ctx.BodyParser(&body); err != nil {
+		return fiber.NewError(fiber.StatusBadRequest, err.Error())
+	}
+	request.Body = &body
+
+	handler := func(ctx *fiber.Ctx, request interface{}) (interface{}, error) {
+		return sh.ssi.URLEncodedExample(ctx.UserContext(), request.(URLEncodedExampleRequestObject))
+	}
+	for _, middleware := range sh.middlewares {
+		handler = middleware(handler, "URLEncodedExample")
+	}
+
+	response, err := handler(ctx, request)
+
+	if err != nil {
+		return fiber.NewError(fiber.StatusBadRequest, err.Error())
+	} else if validResponse, ok := response.(URLEncodedExampleResponseObject); ok {
+		if err := validResponse.VisitURLEncodedExampleResponse(ctx); err != nil {
+			return fiber.NewError(fiber.StatusBadRequest, err.Error())
+		}
+	} else if response != nil {
+		return fmt.Errorf("Unexpected response type: %T", response)
+	}
+	return nil
+}
+
+// HeadersExample operation middleware
+func (sh *strictHandler) HeadersExample(ctx *fiber.Ctx, params HeadersExampleParams) error {
+	var request HeadersExampleRequestObject
+
+	request.Params = params
+
+	var body HeadersExampleJSONRequestBody
+	if err := ctx.BodyParser(&body); err != nil {
+		return fiber.NewError(fiber.StatusBadRequest, err.Error())
+	}
+	request.Body = &body
+
+	handler := func(ctx *fiber.Ctx, request interface{}) (interface{}, error) {
+		return sh.ssi.HeadersExample(ctx.UserContext(), request.(HeadersExampleRequestObject))
+	}
+	for _, middleware := range sh.middlewares {
+		handler = middleware(handler, "HeadersExample")
+	}
+
+	response, err := handler(ctx, request)
+
+	if err != nil {
+		return fiber.NewError(fiber.StatusBadRequest, err.Error())
+	} else if validResponse, ok := response.(HeadersExampleResponseObject); ok {
+		if err := validResponse.VisitHeadersExampleResponse(ctx); err != nil {
+			return fiber.NewError(fiber.StatusBadRequest, err.Error())
+		}
+	} else if response != nil {
+		return fmt.Errorf("Unexpected response type: %T", response)
+	}
+	return nil
+}
+
+// Base64 encoded, gzipped, json marshaled Swagger object
+var swaggerSpec = []string{
+
+	"H4sIAAAAAAAC/+xYX2/bNhD/KgS3p0KynDRPeluDotu6rYOTPg15oMWzzFYiuePJimHouw8UJf+pFS/O",
+	"7BgY+mbRd787/u4Pj1zxzJTWaNDkeLriCM4a7aD9mAqJ8HcFjvyXBJehsqSM5il/J+Sk+6+JOELlxLSA",
+	"Xt3LZ0YT6FZVWFuoTHjV5Ivz+ivusjmUwv/6EWHGU/5DsnElCf+6BB5FaQvgTdNE33jw6SOP+ByEBGy9",
+	"DT+vdrFpaYGn3BEqnXMPEsSuB8WUJsgBvTUv2jnhBXo/0hW3aCwgqcDRQhQVDFvqVsz0C2QUdqD0zOxz",
+	"eWs0CaUdk2o2AwRNrCOPeQzHXGWtQQLJpkvmLWTEHOACkEecFHnH+N32OuscdjziC0AXDF2NxqOxj5ex",
+	"oIVVPOVv26WIW0HzdkPrAFkzFPdf7z79wZRjoiJTClKZKIolKwW6uSgKkExpMt7HKiM34q0pbCP/i+zU",
+	"33dc+rRpM+idkctzZEybmFv5fD0ev1JiNhG/CcaGMNZOJVsV1sLMRFUMkP5Zf9Wm1gwQDXY7S8qqIGUF",
+	"0nawdtn+vRd5DuVrvGRmsIylIHEm1k9l6aLEd81gsEju5qZ2bG5qRoZJEAWrFc1Zr/hNdSvNBHNK5wWw",
+	"3qloMJIFdD33Jy0n3V7uPcbZaynaQXmM67qO2+BVWIDOjAT5MlhVihwSq/NddY8tiKd8uiSftvvd9URJ",
+	"FHGCR0psIZQ+fHS8Ujv5zvTJCjuUK0J7JMo4N/FXWNYGZWwFihII0CUrb73xwDkMlPKfa0mWCc2mwLQo",
+	"QTIxI0D2wbAO0u2V7KSz+8F8DCIbqPa8XX+kf624p6Q9g3nEvQGeBlZCXSv0QSesIDpA28O/5ud/CkDP",
+	"Zpj04h1Tw22wb1Fr6hBmzrfEocgN8BcsTbYkLjMwHM64vdn3Nc4gH8mnz/17eHzWkX/C1vfatX0sYVVY",
+	"fJqzTus5tL2wkz6DxYWSYJLS3hyJfDFSnYVMzRTIuNtFHHx7qiXcGp0h0O4I5O8T2hBbg/lrDs2BBQYi",
+	"5gyrgZWVI2aFc0xR20UKFa5KEvaax+eNZ7fB0v2mnR6K6pszxfTNpSJ6M746XuXtmfNmZ5R5oh4nv70P",
+	"MsfeF082Mx058Z3O7oXK2V9S4q0XleES/jkIbM70DNTCT0RaMgSqUINkCyX6R4C92uwANmEdmoWCG5tp",
+	"qH/dOWYgig5iXfPo0AvQw//4eeKc72bnztMm4uGJKyRLhYWPKJFNkyQ8jY1cLfIccKRMIqzizUPzTwAA",
+	"AP//O0NNuucUAAA=",
+}
+
+// GetSwagger returns the content of the embedded swagger specification file
+// or error if failed to decode
+func decodeSpec() ([]byte, error) {
+	zipped, err := base64.StdEncoding.DecodeString(strings.Join(swaggerSpec, ""))
+	if err != nil {
+		return nil, fmt.Errorf("error base64 decoding spec: %s", err)
+	}
+	zr, err := gzip.NewReader(bytes.NewReader(zipped))
+	if err != nil {
+		return nil, fmt.Errorf("error decompressing spec: %s", err)
+	}
+	var buf bytes.Buffer
+	_, err = buf.ReadFrom(zr)
+	if err != nil {
+		return nil, fmt.Errorf("error decompressing spec: %s", err)
+	}
+
+	return buf.Bytes(), nil
+}
+
+var rawSpec = decodeSpecCached()
+
+// a naive cached of a decoded swagger spec
+func decodeSpecCached() func() ([]byte, error) {
+	data, err := decodeSpec()
+	return func() ([]byte, error) {
+		return data, err
+	}
+}
+
+// Constructs a synthetic filesystem for resolving external references when loading openapi specifications.
+func PathToRawSpec(pathToFile string) map[string]func() ([]byte, error) {
+	var res = make(map[string]func() ([]byte, error))
+	if len(pathToFile) > 0 {
+		res[pathToFile] = rawSpec
+	}
+
+	return res
+}
+
+// GetSwagger returns the Swagger specification corresponding to the generated code
+// in this file. The external references of Swagger specification are resolved.
+// The logic of resolving external references is tightly connected to "import-mapping" feature.
+// Externally referenced files must be embedded in the corresponding golang packages.
+// Urls can be supported but this task was out of the scope.
+func GetSwagger() (swagger *openapi3.T, err error) {
+	var resolvePath = PathToRawSpec("")
+
+	loader := openapi3.NewLoader()
+	loader.IsExternalRefsAllowed = true
+	loader.ReadFromURIFunc = func(loader *openapi3.Loader, url *url.URL) ([]byte, error) {
+		var pathToFile = url.String()
+		pathToFile = path.Clean(pathToFile)
+		getSpec, ok := resolvePath[pathToFile]
+		if !ok {
+			err1 := fmt.Errorf("path not found: %s", pathToFile)
+			return nil, err1
+		}
+		return getSpec()
+	}
+	var specData []byte
+	specData, err = rawSpec()
+	if err != nil {
+		return
+	}
+	swagger, err = loader.LoadFromData(specData)
+	if err != nil {
+		return
+	}
+	return
+}
diff --git a/internal/test/strict-server/fiber/server.go b/internal/test/strict-server/fiber/server.go
new file mode 100644
index 0000000..b0724db
--- /dev/null
+++ b/internal/test/strict-server/fiber/server.go
@@ -0,0 +1,106 @@
+//go:generate go run github.com/deepmap/oapi-codegen/cmd/oapi-codegen --config=server.cfg.yaml ../strict-schema.yaml
+//go:generate go run github.com/deepmap/oapi-codegen/cmd/oapi-codegen --config=types.cfg.yaml ../strict-schema.yaml
+
+package api
+
+import (
+	"context"
+	"io"
+	"mime/multipart"
+)
+
+type StrictServer struct {
+}
+
+func (s StrictServer) JSONExample(ctx context.Context, request JSONExampleRequestObject) (JSONExampleResponseObject, error) {
+	return JSONExample200JSONResponse(*request.Body), nil
+}
+
+func (s StrictServer) MultipartExample(ctx context.Context, request MultipartExampleRequestObject) (MultipartExampleResponseObject, error) {
+	return MultipartExample200MultipartResponse(func(writer *multipart.Writer) error {
+		for {
+			part, err := request.Body.NextPart()
+			if err == io.EOF {
+				return nil
+			} else if err != nil {
+				return err
+			}
+			w, err := writer.CreatePart(part.Header)
+			if err != nil {
+				return err
+			}
+			_, err = io.Copy(w, part)
+			if err != nil {
+				return err
+			}
+			if err = part.Close(); err != nil {
+				return err
+			}
+		}
+	}), nil
+}
+
+func (s StrictServer) MultipleRequestAndResponseTypes(ctx context.Context, request MultipleRequestAndResponseTypesRequestObject) (MultipleRequestAndResponseTypesResponseObject, error) {
+	switch {
+	case request.Body != nil:
+		return MultipleRequestAndResponseTypes200ImagepngResponse{Body: request.Body}, nil
+	case request.JSONBody != nil:
+		return MultipleRequestAndResponseTypes200JSONResponse(*request.JSONBody), nil
+	case request.FormdataBody != nil:
+		return MultipleRequestAndResponseTypes200FormdataResponse(*request.FormdataBody), nil
+	case request.TextBody != nil:
+		return MultipleRequestAndResponseTypes200TextResponse(*request.TextBody), nil
+	case request.MultipartBody != nil:
+		return MultipleRequestAndResponseTypes200MultipartResponse(func(writer *multipart.Writer) error {
+			for {
+				part, err := request.MultipartBody.NextPart()
+				if err == io.EOF {
+					return nil
+				} else if err != nil {
+					return err
+				}
+				w, err := writer.CreatePart(part.Header)
+				if err != nil {
+					return err
+				}
+				_, err = io.Copy(w, part)
+				if err != nil {
+					return err
+				}
+				if err = part.Close(); err != nil {
+					return err
+				}
+			}
+		}), nil
+	default:
+		return MultipleRequestAndResponseTypes400Response{}, nil
+	}
+}
+
+func (s StrictServer) TextExample(ctx context.Context, request TextExampleRequestObject) (TextExampleResponseObject, error) {
+	return TextExample200TextResponse(*request.Body), nil
+}
+
+func (s StrictServer) UnknownExample(ctx context.Context, request UnknownExampleRequestObject) (UnknownExampleResponseObject, error) {
+	return UnknownExample200Videomp4Response{Body: request.Body}, nil
+}
+
+func (s StrictServer) UnspecifiedContentType(ctx context.Context, request UnspecifiedContentTypeRequestObject) (UnspecifiedContentTypeResponseObject, error) {
+	return UnspecifiedContentType200VideoResponse{Body: request.Body, ContentType: request.ContentType}, nil
+}
+
+func (s StrictServer) URLEncodedExample(ctx context.Context, request URLEncodedExampleRequestObject) (URLEncodedExampleResponseObject, error) {
+	return URLEncodedExample200FormdataResponse(*request.Body), nil
+}
+
+func (s StrictServer) HeadersExample(ctx context.Context, request HeadersExampleRequestObject) (HeadersExampleResponseObject, error) {
+	return HeadersExample200JSONResponse{Body: *request.Body, Headers: HeadersExample200ResponseHeaders{Header1: request.Params.Header1, Header2: *request.Params.Header2}}, nil
+}
+
+func (s StrictServer) ReusableResponses(ctx context.Context, request ReusableResponsesRequestObject) (ReusableResponsesResponseObject, error) {
+	return ReusableResponses200JSONResponse{ReusableresponseJSONResponse: ReusableresponseJSONResponse{Body: *request.Body}}, nil
+}
+
+func (s StrictServer) ReservedGoKeywordParameters(ctx context.Context, request ReservedGoKeywordParametersRequestObject) (ReservedGoKeywordParametersResponseObject, error) {
+	return ReservedGoKeywordParameters200TextResponse(""), nil
+}
diff --git a/internal/test/strict-server/fiber/types.cfg.yaml b/internal/test/strict-server/fiber/types.cfg.yaml
new file mode 100644
index 0000000..4ea1d8a
--- /dev/null
+++ b/internal/test/strict-server/fiber/types.cfg.yaml
@@ -0,0 +1,4 @@
+package: api
+generate:
+  models: true
+output: types.gen.go
diff --git a/internal/test/strict-server/fiber/types.gen.go b/internal/test/strict-server/fiber/types.gen.go
new file mode 100644
index 0000000..33827cb
--- /dev/null
+++ b/internal/test/strict-server/fiber/types.gen.go
@@ -0,0 +1,54 @@
+// Package api provides primitives to interact with the openapi HTTP API.
+//
+// Code generated by github.com/deepmap/oapi-codegen version (devel) DO NOT EDIT.
+package api
+
+// Example defines model for example.
+type Example struct {
+	Value *string `json:"value,omitempty"`
+}
+
+// Reusableresponse defines model for reusableresponse.
+type Reusableresponse = Example
+
+// MultipleRequestAndResponseTypesTextBody defines parameters for MultipleRequestAndResponseTypes.
+type MultipleRequestAndResponseTypesTextBody = string
+
+// TextExampleTextBody defines parameters for TextExample.
+type TextExampleTextBody = string
+
+// HeadersExampleParams defines parameters for HeadersExample.
+type HeadersExampleParams struct {
+	Header1 string `json:"header1"`
+	Header2 *int   `json:"header2,omitempty"`
+}
+
+// JSONExampleJSONRequestBody defines body for JSONExample for application/json ContentType.
+type JSONExampleJSONRequestBody = Example
+
+// MultipartExampleMultipartRequestBody defines body for MultipartExample for multipart/form-data ContentType.
+type MultipartExampleMultipartRequestBody = Example
+
+// MultipleRequestAndResponseTypesJSONRequestBody defines body for MultipleRequestAndResponseTypes for application/json ContentType.
+type MultipleRequestAndResponseTypesJSONRequestBody = Example
+
+// MultipleRequestAndResponseTypesFormdataRequestBody defines body for MultipleRequestAndResponseTypes for application/x-www-form-urlencoded ContentType.
+type MultipleRequestAndResponseTypesFormdataRequestBody = Example
+
+// MultipleRequestAndResponseTypesMultipartRequestBody defines body for MultipleRequestAndResponseTypes for multipart/form-data ContentType.
+type MultipleRequestAndResponseTypesMultipartRequestBody = Example
+
+// MultipleRequestAndResponseTypesTextRequestBody defines body for MultipleRequestAndResponseTypes for text/plain ContentType.
+type MultipleRequestAndResponseTypesTextRequestBody = MultipleRequestAndResponseTypesTextBody
+
+// ReusableResponsesJSONRequestBody defines body for ReusableResponses for application/json ContentType.
+type ReusableResponsesJSONRequestBody = Example
+
+// TextExampleTextRequestBody defines body for TextExample for text/plain ContentType.
+type TextExampleTextRequestBody = TextExampleTextBody
+
+// URLEncodedExampleFormdataRequestBody defines body for URLEncodedExample for application/x-www-form-urlencoded ContentType.
+type URLEncodedExampleFormdataRequestBody = Example
+
+// HeadersExampleJSONRequestBody defines body for HeadersExample for application/json ContentType.
+type HeadersExampleJSONRequestBody = Example
diff --git a/internal/test/strict-server/gin/server.gen.go b/internal/test/strict-server/gin/server.gen.go
index 618db70..dd9839c 100644
--- a/internal/test/strict-server/gin/server.gen.go
+++ b/internal/test/strict-server/gin/server.gen.go
@@ -221,7 +221,7 @@ func (siw *ServerInterfaceWrapper) HeadersExample(c *gin.Context) {
 		params.Header1 = Header1
 
 	} else {
-		siw.ErrorHandler(c, fmt.Errorf("Header parameter header1 is required, but not found: %s", err), http.StatusBadRequest)
+		siw.ErrorHandler(c, fmt.Errorf("Header parameter header1 is required, but not found"), http.StatusBadRequest)
 		return
 	}
 
@@ -763,9 +763,8 @@ type StrictServerInterface interface {
 	HeadersExample(ctx context.Context, request HeadersExampleRequestObject) (HeadersExampleResponseObject, error)
 }
 
-type StrictHandlerFunc func(ctx *gin.Context, args interface{}) (interface{}, error)
-
-type StrictMiddlewareFunc func(f StrictHandlerFunc, operationID string) StrictHandlerFunc
+type StrictHandlerFunc = runtime.StrictGinHandlerFunc
+type StrictMiddlewareFunc = runtime.StrictGinMiddlewareFunc
 
 func NewStrictHandler(ssi StrictServerInterface, middlewares []StrictMiddlewareFunc) ServerInterface {
 	return &strictHandler{ssi: ssi, middlewares: middlewares}
@@ -799,6 +798,7 @@ func (sh *strictHandler) JSONExample(ctx *gin.Context) {
 
 	if err != nil {
 		ctx.Error(err)
+		ctx.Status(http.StatusInternalServerError)
 	} else if validResponse, ok := response.(JSONExampleResponseObject); ok {
 		if err := validResponse.VisitJSONExampleResponse(ctx.Writer); err != nil {
 			ctx.Error(err)
@@ -830,6 +830,7 @@ func (sh *strictHandler) MultipartExample(ctx *gin.Context) {
 
 	if err != nil {
 		ctx.Error(err)
+		ctx.Status(http.StatusInternalServerError)
 	} else if validResponse, ok := response.(MultipartExampleResponseObject); ok {
 		if err := validResponse.VisitMultipartExampleResponse(ctx.Writer); err != nil {
 			ctx.Error(err)
@@ -896,6 +897,7 @@ func (sh *strictHandler) MultipleRequestAndResponseTypes(ctx *gin.Context) {
 
 	if err != nil {
 		ctx.Error(err)
+		ctx.Status(http.StatusInternalServerError)
 	} else if validResponse, ok := response.(MultipleRequestAndResponseTypesResponseObject); ok {
 		if err := validResponse.VisitMultipleRequestAndResponseTypesResponse(ctx.Writer); err != nil {
 			ctx.Error(err)
@@ -922,6 +924,7 @@ func (sh *strictHandler) ReservedGoKeywordParameters(ctx *gin.Context, pType str
 
 	if err != nil {
 		ctx.Error(err)
+		ctx.Status(http.StatusInternalServerError)
 	} else if validResponse, ok := response.(ReservedGoKeywordParametersResponseObject); ok {
 		if err := validResponse.VisitReservedGoKeywordParametersResponse(ctx.Writer); err != nil {
 			ctx.Error(err)
@@ -954,6 +957,7 @@ func (sh *strictHandler) ReusableResponses(ctx *gin.Context) {
 
 	if err != nil {
 		ctx.Error(err)
+		ctx.Status(http.StatusInternalServerError)
 	} else if validResponse, ok := response.(ReusableResponsesResponseObject); ok {
 		if err := validResponse.VisitReusableResponsesResponse(ctx.Writer); err != nil {
 			ctx.Error(err)
@@ -986,6 +990,7 @@ func (sh *strictHandler) TextExample(ctx *gin.Context) {
 
 	if err != nil {
 		ctx.Error(err)
+		ctx.Status(http.StatusInternalServerError)
 	} else if validResponse, ok := response.(TextExampleResponseObject); ok {
 		if err := validResponse.VisitTextExampleResponse(ctx.Writer); err != nil {
 			ctx.Error(err)
@@ -1012,6 +1017,7 @@ func (sh *strictHandler) UnknownExample(ctx *gin.Context) {
 
 	if err != nil {
 		ctx.Error(err)
+		ctx.Status(http.StatusInternalServerError)
 	} else if validResponse, ok := response.(UnknownExampleResponseObject); ok {
 		if err := validResponse.VisitUnknownExampleResponse(ctx.Writer); err != nil {
 			ctx.Error(err)
@@ -1040,6 +1046,7 @@ func (sh *strictHandler) UnspecifiedContentType(ctx *gin.Context) {
 
 	if err != nil {
 		ctx.Error(err)
+		ctx.Status(http.StatusInternalServerError)
 	} else if validResponse, ok := response.(UnspecifiedContentTypeResponseObject); ok {
 		if err := validResponse.VisitUnspecifiedContentTypeResponse(ctx.Writer); err != nil {
 			ctx.Error(err)
@@ -1075,6 +1082,7 @@ func (sh *strictHandler) URLEncodedExample(ctx *gin.Context) {
 
 	if err != nil {
 		ctx.Error(err)
+		ctx.Status(http.StatusInternalServerError)
 	} else if validResponse, ok := response.(URLEncodedExampleResponseObject); ok {
 		if err := validResponse.VisitURLEncodedExampleResponse(ctx.Writer); err != nil {
 			ctx.Error(err)
@@ -1109,6 +1117,7 @@ func (sh *strictHandler) HeadersExample(ctx *gin.Context, params HeadersExampleP
 
 	if err != nil {
 		ctx.Error(err)
+		ctx.Status(http.StatusInternalServerError)
 	} else if validResponse, ok := response.(HeadersExampleResponseObject); ok {
 		if err := validResponse.VisitHeadersExampleResponse(ctx.Writer); err != nil {
 			ctx.Error(err)
diff --git a/internal/test/strict-server/strict_test.go b/internal/test/strict-server/strict_test.go
index 860cc4c..bfbb9bc 100644
--- a/internal/test/strict-server/strict_test.go
+++ b/internal/test/strict-server/strict_test.go
@@ -13,57 +13,68 @@ import (
 
 	"github.com/gin-gonic/gin"
 	"github.com/go-chi/chi/v5"
+	"github.com/gofiber/fiber/v2"
+	"github.com/gofiber/fiber/v2/middleware/adaptor"
 	"github.com/labstack/echo/v4"
 	"github.com/stretchr/testify/assert"
 
-	"github.com/deepmap/oapi-codegen/internal/test/strict-server/chi"
-	api3 "github.com/deepmap/oapi-codegen/internal/test/strict-server/client"
-	api4 "github.com/deepmap/oapi-codegen/internal/test/strict-server/echo"
-	api2 "github.com/deepmap/oapi-codegen/internal/test/strict-server/gin"
+	chiAPI "github.com/deepmap/oapi-codegen/internal/test/strict-server/chi"
+	clientAPI "github.com/deepmap/oapi-codegen/internal/test/strict-server/client"
+	echoAPI "github.com/deepmap/oapi-codegen/internal/test/strict-server/echo"
+	fiberAPI "github.com/deepmap/oapi-codegen/internal/test/strict-server/fiber"
+	ginAPI "github.com/deepmap/oapi-codegen/internal/test/strict-server/gin"
 	"github.com/deepmap/oapi-codegen/pkg/runtime"
 	"github.com/deepmap/oapi-codegen/pkg/testutil"
 )
 
 func TestChiServer(t *testing.T) {
-	server := api.StrictServer{}
-	strictHandler := api.NewStrictHandler(server, nil)
+	server := chiAPI.StrictServer{}
+	strictHandler := chiAPI.NewStrictHandler(server, nil)
 	r := chi.NewRouter()
-	handler := api.HandlerFromMux(strictHandler, r)
+	handler := chiAPI.HandlerFromMux(strictHandler, r)
 	testImpl(t, handler)
 }
 
 func TestEchoServer(t *testing.T) {
-	server := api4.StrictServer{}
-	strictHandler := api4.NewStrictHandler(server, nil)
+	server := echoAPI.StrictServer{}
+	strictHandler := echoAPI.NewStrictHandler(server, nil)
 	e := echo.New()
-	api4.RegisterHandlers(e, strictHandler)
+	echoAPI.RegisterHandlers(e, strictHandler)
 	testImpl(t, e)
 }
 
 func TestGinServer(t *testing.T) {
-	server := api2.StrictServer{}
-	strictHandler := api2.NewStrictHandler(server, nil)
+	server := ginAPI.StrictServer{}
+	strictHandler := ginAPI.NewStrictHandler(server, nil)
 	gin.SetMode(gin.ReleaseMode)
 	r := gin.New()
-	api2.RegisterHandlers(r, strictHandler)
+	ginAPI.RegisterHandlers(r, strictHandler)
 	testImpl(t, r)
 }
 
+func TestFiberServer(t *testing.T) {
+	server := fiberAPI.StrictServer{}
+	strictHandler := fiberAPI.NewStrictHandler(server, nil)
+	r := fiber.New()
+	fiberAPI.RegisterHandlers(r, strictHandler)
+	testImpl(t, adaptor.FiberApp(r))
+}
+
 func testImpl(t *testing.T, handler http.Handler) {
 	t.Run("JSONExample", func(t *testing.T) {
 		value := "123"
-		requestBody := api3.Example{Value: &value}
+		requestBody := clientAPI.Example{Value: &value}
 		rr := testutil.NewRequest().Post("/json").WithJsonBody(requestBody).GoWithHTTPHandler(t, handler).Recorder
 		assert.Equal(t, http.StatusOK, rr.Code)
 		assert.True(t, strings.HasPrefix(rr.Header().Get("Content-Type"), "application/json"))
-		var responseBody api3.Example
+		var responseBody clientAPI.Example
 		err := json.NewDecoder(rr.Body).Decode(&responseBody)
 		assert.NoError(t, err)
 		assert.Equal(t, requestBody, responseBody)
 	})
 	t.Run("URLEncodedExample", func(t *testing.T) {
 		value := "456"
-		requestBody := api3.Example{Value: &value}
+		requestBody := clientAPI.Example{Value: &value}
 		requestBodyEncoded, err := runtime.MarshalForm(&requestBody, nil)
 		assert.NoError(t, err)
 		rr := testutil.NewRequest().Post("/urlencoded").WithContentType("application/x-www-form-urlencoded").WithBody([]byte(requestBodyEncoded.Encode())).GoWithHTTPHandler(t, handler).Recorder
@@ -71,7 +82,7 @@ func testImpl(t *testing.T, handler http.Handler) {
 		assert.Equal(t, "application/x-www-form-urlencoded", rr.Header().Get("Content-Type"))
 		values, err := url.ParseQuery(rr.Body.String())
 		assert.NoError(t, err)
-		var responseBody api3.Example
+		var responseBody clientAPI.Example
 		err = runtime.BindForm(&responseBody, values, nil, nil)
 		assert.NoError(t, err)
 		assert.Equal(t, requestBody, responseBody)
@@ -116,18 +127,18 @@ func testImpl(t *testing.T, handler http.Handler) {
 	})
 	t.Run("MultipleRequestAndResponseTypesJSON", func(t *testing.T) {
 		value := "123"
-		requestBody := api3.Example{Value: &value}
+		requestBody := clientAPI.Example{Value: &value}
 		rr := testutil.NewRequest().Post("/multiple").WithJsonBody(requestBody).GoWithHTTPHandler(t, handler).Recorder
 		assert.Equal(t, http.StatusOK, rr.Code)
 		assert.True(t, strings.HasPrefix(rr.Header().Get("Content-Type"), "application/json"))
-		var responseBody api3.Example
+		var responseBody clientAPI.Example
 		err := json.NewDecoder(rr.Body).Decode(&responseBody)
 		assert.NoError(t, err)
 		assert.Equal(t, requestBody, responseBody)
 	})
 	t.Run("MultipleRequestAndResponseTypesFormdata", func(t *testing.T) {
 		value := "456"
-		requestBody := api3.Example{Value: &value}
+		requestBody := clientAPI.Example{Value: &value}
 		requestBodyEncoded, err := runtime.MarshalForm(&requestBody, nil)
 		assert.NoError(t, err)
 		rr := testutil.NewRequest().Post("/multiple").WithContentType("application/x-www-form-urlencoded").WithBody([]byte(requestBodyEncoded.Encode())).GoWithHTTPHandler(t, handler).Recorder
@@ -135,7 +146,7 @@ func testImpl(t *testing.T, handler http.Handler) {
 		assert.Equal(t, "application/x-www-form-urlencoded", rr.Header().Get("Content-Type"))
 		values, err := url.ParseQuery(rr.Body.String())
 		assert.NoError(t, err)
-		var responseBody api3.Example
+		var responseBody clientAPI.Example
 		err = runtime.BindForm(&responseBody, values, nil, nil)
 		assert.NoError(t, err)
 		assert.Equal(t, requestBody, responseBody)
@@ -182,11 +193,11 @@ func testImpl(t *testing.T, handler http.Handler) {
 		header1 := "value1"
 		header2 := "890"
 		value := "asdf"
-		requestBody := api3.Example{Value: &value}
+		requestBody := clientAPI.Example{Value: &value}
 		rr := testutil.NewRequest().Post("/with-headers").WithHeader("header1", header1).WithHeader("header2", header2).WithJsonBody(requestBody).GoWithHTTPHandler(t, handler).Recorder
 		assert.Equal(t, http.StatusOK, rr.Code)
 		assert.True(t, strings.HasPrefix(rr.Header().Get("Content-Type"), "application/json"))
-		var responseBody api3.Example
+		var responseBody clientAPI.Example
 		err := json.NewDecoder(rr.Body).Decode(&responseBody)
 		assert.NoError(t, err)
 		assert.Equal(t, requestBody, responseBody)
@@ -203,11 +214,11 @@ func testImpl(t *testing.T, handler http.Handler) {
 	})
 	t.Run("ReusableResponses", func(t *testing.T) {
 		value := "jkl;"
-		requestBody := api3.Example{Value: &value}
+		requestBody := clientAPI.Example{Value: &value}
 		rr := testutil.NewRequest().Post("/reusable-responses").WithJsonBody(requestBody).GoWithHTTPHandler(t, handler).Recorder
 		assert.Equal(t, http.StatusOK, rr.Code)
 		assert.True(t, strings.HasPrefix(rr.Header().Get("Content-Type"), "application/json"))
-		var responseBody api3.Example
+		var responseBody clientAPI.Example
 		err := json.NewDecoder(rr.Body).Decode(&responseBody)
 		assert.NoError(t, err)
 		assert.Equal(t, requestBody, responseBody)
diff --git a/pkg/codegen/codegen_test.go b/pkg/codegen/codegen_test.go
index 6ae6121..a127bf0 100644
--- a/pkg/codegen/codegen_test.go
+++ b/pkg/codegen/codegen_test.go
@@ -3,8 +3,10 @@ package codegen
 import (
 	"bytes"
 	_ "embed"
+	"fmt"
 	"go/format"
 	"io"
+	"net"
 	"net/http"
 	"testing"
 
@@ -77,7 +79,7 @@ func TestExamplePetStoreCodeGeneration(t *testing.T) {
 
 func TestExamplePetStoreCodeGenerationWithUserTemplates(t *testing.T) {
 
-	userTemplates := map[string]string{"typedef.tmpl": "//blah"}
+	userTemplates := map[string]string{"typedef.tmpl": "//blah\n//blah"}
 
 	// Input vars for code generation:
 	packageName := "api"
@@ -107,7 +109,92 @@ func TestExamplePetStoreCodeGenerationWithUserTemplates(t *testing.T) {
 	// Check that we have a package:
 	assert.Contains(t, code, "package api")
 
-	// Check that the built-in template has been overriden
+	// Check that the built-in template has been overridden
+	assert.Contains(t, code, "//blah")
+}
+
+func TestExamplePetStoreCodeGenerationWithFileUserTemplates(t *testing.T) {
+
+	userTemplates := map[string]string{"typedef.tmpl": "./templates/typedef.tmpl"}
+
+	// Input vars for code generation:
+	packageName := "api"
+	opts := Configuration{
+		PackageName: packageName,
+		Generate: GenerateOptions{
+			Models: true,
+		},
+		OutputOptions: OutputOptions{
+			UserTemplates: userTemplates,
+		},
+	}
+
+	// Get a spec from the example PetStore definition:
+	swagger, err := examplePetstore.GetSwagger()
+	assert.NoError(t, err)
+
+	// Run our code generation:
+	code, err := Generate(swagger, opts)
+	assert.NoError(t, err)
+	assert.NotEmpty(t, code)
+
+	// Check that we have valid (formattable) code:
+	_, err = format.Source([]byte(code))
+	assert.NoError(t, err)
+
+	// Check that we have a package:
+	assert.Contains(t, code, "package api")
+
+	// Check that the built-in template has been overridden
+	assert.Contains(t, code, "// Package api provides primitives to interact with the openapi")
+}
+
+func TestExamplePetStoreCodeGenerationWithHTTPUserTemplates(t *testing.T) {
+
+	ln, err := net.Listen("tcp", "127.0.0.1:0")
+	assert.NoError(t, err)
+	defer ln.Close()
+
+	//nolint:errcheck
+	//Does not matter if the server returns an error on close etc.
+	go http.Serve(ln, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		_, writeErr := w.Write([]byte("//blah"))
+		assert.NoError(t, writeErr)
+	}))
+
+	t.Logf("Listening on %s", ln.Addr().String())
+
+	userTemplates := map[string]string{"typedef.tmpl": fmt.Sprintf("http://%s", ln.Addr().String())}
+
+	// Input vars for code generation:
+	packageName := "api"
+	opts := Configuration{
+		PackageName: packageName,
+		Generate: GenerateOptions{
+			Models: true,
+		},
+		OutputOptions: OutputOptions{
+			UserTemplates: userTemplates,
+		},
+	}
+
+	// Get a spec from the example PetStore definition:
+	swagger, err := examplePetstore.GetSwagger()
+	assert.NoError(t, err)
+
+	// Run our code generation:
+	code, err := Generate(swagger, opts)
+	assert.NoError(t, err)
+	assert.NotEmpty(t, code)
+
+	// Check that we have valid (formattable) code:
+	_, err = format.Source([]byte(code))
+	assert.NoError(t, err)
+
+	// Check that we have a package:
+	assert.Contains(t, code, "package api")
+
+	// Check that the built-in template has been overridden
 	assert.Contains(t, code, "//blah")
 }
 
diff --git a/pkg/codegen/operations_test.go b/pkg/codegen/operations_test.go
index 0eff03e..77a54a8 100644
--- a/pkg/codegen/operations_test.go
+++ b/pkg/codegen/operations_test.go
@@ -131,7 +131,7 @@ func TestGenerateDefaultOperationID(t *testing.T) {
 	}
 
 	for _, test := range suite {
-		got, err := generateDefaultOperationID(test.op, test.path)
+		got, err := generateDefaultOperationID(test.op, test.path, ToCamelCase)
 		if err != nil {
 			if !test.wantErr {
 				t.Fatalf("did not expected error but got %v", err)
diff --git a/pkg/codegen/utils_test.go b/pkg/codegen/utils_test.go
index 32708f0..6776526 100644
--- a/pkg/codegen/utils_test.go
+++ b/pkg/codegen/utils_test.go
@@ -134,12 +134,12 @@ func TestSortedRequestBodyKeys(t *testing.T) {
 }
 
 func TestRefPathToGoType(t *testing.T) {
-	old := importMapping
-	importMapping = constructImportMapping(map[string]string{
+	old := globalState.importMapping
+	globalState.importMapping = constructImportMapping(map[string]string{
 		"doc.json":                    "externalref0",
 		"http://deepmap.com/doc.json": "externalref1",
 	})
-	defer func() { importMapping = old }()
+	defer func() { globalState.importMapping = old }()
 
 	tests := []struct {
 		name   string
@@ -280,6 +280,23 @@ func TestSwaggerUriToGorillaUri(t *testing.T) { // TODO
 	assert.Equal(t, "/path/{arg}/foo", SwaggerUriToGorillaUri("/path/{?arg*}/foo"))
 }
 
+func TestSwaggerUriToFiberUri(t *testing.T) {
+	assert.Equal(t, "/path", SwaggerUriToFiberUri("/path"))
+	assert.Equal(t, "/path/:arg", SwaggerUriToFiberUri("/path/{arg}"))
+	assert.Equal(t, "/path/:arg1/:arg2", SwaggerUriToFiberUri("/path/{arg1}/{arg2}"))
+	assert.Equal(t, "/path/:arg1/:arg2/foo", SwaggerUriToFiberUri("/path/{arg1}/{arg2}/foo"))
+
+	// Make sure all the exploded and alternate formats match too
+	assert.Equal(t, "/path/:arg/foo", SwaggerUriToFiberUri("/path/{arg}/foo"))
+	assert.Equal(t, "/path/:arg/foo", SwaggerUriToFiberUri("/path/{arg*}/foo"))
+	assert.Equal(t, "/path/:arg/foo", SwaggerUriToFiberUri("/path/{.arg}/foo"))
+	assert.Equal(t, "/path/:arg/foo", SwaggerUriToFiberUri("/path/{.arg*}/foo"))
+	assert.Equal(t, "/path/:arg/foo", SwaggerUriToFiberUri("/path/{;arg}/foo"))
+	assert.Equal(t, "/path/:arg/foo", SwaggerUriToFiberUri("/path/{;arg*}/foo"))
+	assert.Equal(t, "/path/:arg/foo", SwaggerUriToFiberUri("/path/{?arg}/foo"))
+	assert.Equal(t, "/path/:arg/foo", SwaggerUriToFiberUri("/path/{?arg*}/foo"))
+}
+
 func TestOrderedParamsFromUri(t *testing.T) {
 	result := OrderedParamsFromUri("/path/{param1}/{.param2}/{;param3*}/foo")
 	assert.EqualValues(t, []string{"param1", "param2", "param3"}, result)
@@ -433,6 +450,18 @@ func TestSchemaNameToTypeName(t *testing.T) {
 	}
 }
 
+
+func TestTypeDefinitionsEquivalent(t *testing.T) {
+	def1 := TypeDefinition{TypeName: "name", Schema: Schema{
+		OAPISchema: &openapi3.Schema{},
+	}}
+	def2 := TypeDefinition{TypeName: "name", Schema: Schema{
+		OAPISchema: &openapi3.Schema{},
+	}}
+	assert.True(t, TypeDefinitionsEquivalent(def1, def2))
+}
+
+
 func TestRefPathToObjName(t *testing.T) {
 	t.Parallel()
 
@@ -446,3 +475,45 @@ func TestRefPathToObjName(t *testing.T) {
 		assert.Equal(t, want, RefPathToObjName(in))
 	}
 }
+
+func Test_replaceInitialisms(t *testing.T) {
+	type args struct {
+		s string
+	}
+	tests := []struct {
+		name string
+		args args
+		want string
+	}{
+		{
+			name: "empty string",
+			args: args{s: ""},
+			want: "",
+		},
+		{
+			name: "no initialism",
+			args: args{s: "foo"},
+			want: "foo",
+		},
+		{
+			name: "one initialism",
+			args: args{s: "fooId"},
+			want: "fooID",
+		},
+		{
+			name: "two initialism",
+			args: args{s: "fooIdBarApi"},
+			want: "fooIDBarAPI",
+		},
+		{
+			name: "already initialism",
+			args: args{s: "fooIDBarAPI"},
+			want: "fooIDBarAPI",
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			assert.Equalf(t, tt.want, replaceInitialism(tt.args.s), "replaceInitialism(%v)", tt.args.s)
+		})
+	}
+}
diff --git a/pkg/fiber-middleware/oapi_validate_test.go b/pkg/fiber-middleware/oapi_validate_test.go
new file mode 100644
index 0000000..9201e13
--- /dev/null
+++ b/pkg/fiber-middleware/oapi_validate_test.go
@@ -0,0 +1,434 @@
+package middleware
+
+import (
+	"bytes"
+	"context"
+	_ "embed"
+	"encoding/json"
+	"errors"
+	"fmt"
+	"io"
+	"net/http"
+	"net/http/httptest"
+	"net/url"
+	"testing"
+
+	"github.com/getkin/kin-openapi/openapi3"
+	"github.com/getkin/kin-openapi/openapi3filter"
+	"github.com/gofiber/fiber/v2"
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+)
+
+//go:embed test_spec.yaml
+var testSchema []byte
+
+func doGet(t *testing.T, app *fiber.App, rawURL string) *http.Response {
+	t.Helper()
+
+	u, err := url.Parse(rawURL)
+	if err != nil {
+		t.Fatalf("invalid URL %q: %v", rawURL, err)
+	}
+
+	req := httptest.NewRequest("GET", u.RequestURI(), nil)
+	req.Header.Add("Accept", "application/json")
+	req.Host = u.Host
+
+	r, err := app.Test(req)
+	if err != nil {
+		t.Fatalf("Failed to test request, URL=%q: %v", rawURL, err)
+	}
+
+	return r
+}
+
+func doPost(t *testing.T, app *fiber.App, rawURL string, jsonBody interface{}) *http.Response {
+	t.Helper()
+
+	u, err := url.Parse(rawURL)
+	if err != nil {
+		t.Fatalf("invalid url %q: %v", rawURL, err)
+	}
+
+	buf, err := json.Marshal(jsonBody)
+	if err != nil {
+		t.Fatalf("failed to marshal: %v", err)
+	}
+
+	req := httptest.NewRequest("POST", u.RequestURI(), bytes.NewReader(buf))
+	req.Header.Add("Accept", "application/json")
+	req.Header.Add("Content-Type", "application/json")
+	req.Host = u.Host
+
+	r, err := app.Test(req)
+	if err != nil {
+		t.Fatalf("Failed to test request, URL=%q: %v", rawURL, err)
+	}
+
+	return r
+}
+
+func TestOapiRequestValidator(t *testing.T) {
+
+	swagger, err := openapi3.NewLoader().LoadFromData(testSchema)
+	require.NoError(t, err, "Error initializing swagger")
+
+	// Create a new fiber router
+	app := fiber.New()
+
+	// Set up an authenticator to check authenticated function. It will allow
+	// access to "someScope", but disallow others.
+	options := Options{
+		ErrorHandler: func(c *fiber.Ctx, message string, statusCode int) {
+			_ = c.Status(statusCode).SendString("test: " + message)
+		},
+		Options: openapi3filter.Options{
+			AuthenticationFunc: func(c context.Context, input *openapi3filter.AuthenticationInput) error {
+				// The fiber context should be propagated into here.
+				gCtx := GetFiberContext(c)
+				assert.NotNil(t, gCtx)
+				// As should user data
+				assert.EqualValues(t, "hi!", GetUserData(c))
+
+				for _, s := range input.Scopes {
+					if s == "someScope" {
+						return nil
+					}
+					if s == "unauthorized" {
+						return errors.New("unauthorized")
+					}
+				}
+				return errors.New("forbidden")
+			},
+		},
+		UserData: "hi!",
+	}
+
+	// Install our OpenApi based request validator
+	app.Use(OapiRequestValidatorWithOptions(swagger, &options))
+
+	called := false
+
+	// Install a request handler for /resource. We want to make sure it doesn't
+	// get called.
+	app.Get("/resource", func(c *fiber.Ctx) error {
+		called = true
+		return nil
+	})
+	// Let's send the request to the wrong server, this should fail validation
+	{
+		res := doGet(t, app, "https://not.deepmap.ai/resource")
+		assert.Equal(t, http.StatusBadRequest, res.StatusCode)
+		assert.False(t, called, "Handler should not have been called")
+	}
+
+	// Let's send a good request, it should pass
+	{
+		res := doGet(t, app, "https://deepmap.ai/resource")
+		assert.Equal(t, http.StatusOK, res.StatusCode)
+		assert.True(t, called, "Handler should have been called")
+		called = false
+	}
+
+	// Send an out-of-spec parameter
+	{
+		res := doGet(t, app, "https://deepmap.ai/resource?id=500")
+		assert.Equal(t, http.StatusBadRequest, res.StatusCode)
+		assert.False(t, called, "Handler should not have been called")
+		called = false
+	}
+
+	// Send a bad parameter type
+	{
+		res := doGet(t, app, "https://deepmap.ai/resource?id=foo")
+		assert.Equal(t, http.StatusBadRequest, res.StatusCode)
+		assert.False(t, called, "Handler should not have been called")
+		called = false
+	}
+
+	// Add a handler for the POST message
+	app.Post("/resource", func(c *fiber.Ctx) error {
+		called = true
+		return c.SendStatus(http.StatusNoContent)
+	})
+
+	called = false
+	// Send a good request body
+	{
+		body := struct {
+			Name string `json:"name"`
+		}{
+			Name: "Marcin",
+		}
+		res := doPost(t, app, "https://deepmap.ai/resource", body)
+		assert.Equal(t, http.StatusNoContent, res.StatusCode)
+		assert.True(t, called, "Handler should have been called")
+		called = false
+	}
+
+	// Send a malformed body
+	{
+		body := struct {
+			Name int `json:"name"`
+		}{
+			Name: 7,
+		}
+		res := doPost(t, app, "https://deepmap.ai/resource", body)
+		assert.Equal(t, http.StatusBadRequest, res.StatusCode)
+		assert.False(t, called, "Handler should not have been called")
+		called = false
+	}
+
+	app.Get("/protected_resource", func(c *fiber.Ctx) error {
+		called = true
+		return c.SendStatus(http.StatusNoContent)
+	})
+
+	// Call a protected function to which we have access
+	{
+		res := doGet(t, app, "https://deepmap.ai/protected_resource")
+		assert.Equal(t, http.StatusNoContent, res.StatusCode)
+		assert.True(t, called, "Handler should have been called")
+		called = false
+	}
+
+	app.Get("/protected_resource2", func(c *fiber.Ctx) error {
+		called = true
+		return c.SendStatus(http.StatusNoContent)
+	})
+	// Call a protected function to which we don't have access
+	{
+		res := doGet(t, app, "https://deepmap.ai/protected_resource2")
+		assert.Equal(t, http.StatusBadRequest, res.StatusCode)
+		assert.False(t, called, "Handler should not have been called")
+		called = false
+	}
+
+	app.Get("/protected_resource_401", func(c *fiber.Ctx) error {
+		called = true
+		return c.SendStatus(http.StatusNoContent)
+	})
+	// Call a protected function without credentials
+	{
+		res := doGet(t, app, "https://deepmap.ai/protected_resource_401")
+		assert.Equal(t, http.StatusBadRequest, res.StatusCode)
+		body, err := io.ReadAll(res.Body)
+		if assert.NoError(t, err) {
+			assert.Equal(t, "test: error in openapi3filter.SecurityRequirementsError: security requirements failed: unauthorized", string(body))
+		}
+		assert.False(t, called, "Handler should not have been called")
+		called = false
+	}
+}
+
+func TestOapiRequestValidatorWithOptionsMultiError(t *testing.T) {
+	swagger, err := openapi3.NewLoader().LoadFromData(testSchema)
+	require.NoError(t, err, "Error initializing swagger")
+
+	app := fiber.New()
+
+	// Set up an authenticator to check authenticated function. It will allow
+	// access to "someScope", but disallow others.
+	options := Options{
+		Options: openapi3filter.Options{
+			ExcludeRequestBody:    false,
+			ExcludeResponseBody:   false,
+			IncludeResponseStatus: true,
+			MultiError:            true,
+		},
+	}
+
+	// register middleware
+	app.Use(OapiRequestValidatorWithOptions(swagger, &options))
+
+	called := false
+
+	// Install a request handler for /resource. We want to make sure it doesn't
+	// get called.
+	app.Get("/multiparamresource", func(c *fiber.Ctx) error {
+		called = true
+		return nil
+	})
+
+	// Let's send a good request, it should pass
+	{
+		res := doGet(t, app, "https://deepmap.ai/multiparamresource?id=50&id2=50")
+		assert.Equal(t, http.StatusOK, res.StatusCode)
+		assert.True(t, called, "Handler should have been called")
+		called = false
+	}
+
+	// Let's send a request with a missing parameter, it should return
+	// a bad status
+	{
+		res := doGet(t, app, "https://deepmap.ai/multiparamresource?id=50")
+		assert.Equal(t, http.StatusBadRequest, res.StatusCode)
+		body, err := io.ReadAll(res.Body)
+		if assert.NoError(t, err) {
+			assert.Contains(t, string(body), "multiple errors encountered")
+			assert.Contains(t, string(body), "parameter \"id2\"")
+			assert.Contains(t, string(body), "value is required but missing")
+		}
+		assert.False(t, called, "Handler should not have been called")
+		called = false
+	}
+
+	// Let's send a request with a 2 missing parameters, it should return
+	// a bad status
+	{
+		res := doGet(t, app, "https://deepmap.ai/multiparamresource")
+		assert.Equal(t, http.StatusBadRequest, res.StatusCode)
+		body, err := io.ReadAll(res.Body)
+		if assert.NoError(t, err) {
+			assert.Contains(t, string(body), "multiple errors encountered")
+			assert.Contains(t, string(body), "parameter \"id\"")
+			assert.Contains(t, string(body), "value is required but missing")
+			assert.Contains(t, string(body), "parameter \"id2\"")
+			assert.Contains(t, string(body), "value is required but missing")
+		}
+		assert.False(t, called, "Handler should not have been called")
+		called = false
+	}
+
+	// Let's send a request with a 1 missing parameter, and another outside
+	// or the parameters. It should return a bad status
+	{
+		res := doGet(t, app, "https://deepmap.ai/multiparamresource?id=500")
+		assert.Equal(t, http.StatusBadRequest, res.StatusCode)
+		body, err := io.ReadAll(res.Body)
+		if assert.NoError(t, err) {
+			assert.Contains(t, string(body), "multiple errors encountered")
+			assert.Contains(t, string(body), "parameter \"id\"")
+			assert.Contains(t, string(body), "number must be at most 100")
+			assert.Contains(t, string(body), "parameter \"id2\"")
+			assert.Contains(t, string(body), "value is required but missing")
+		}
+		assert.False(t, called, "Handler should not have been called")
+		called = false
+	}
+
+	// Let's send a request with a parameters that do not meet spec. It should
+	// return a bad status
+	{
+		res := doGet(t, app, "https://deepmap.ai/multiparamresource?id=abc&id2=1")
+		assert.Equal(t, http.StatusBadRequest, res.StatusCode)
+		body, err := io.ReadAll(res.Body)
+		if assert.NoError(t, err) {
+			assert.Contains(t, string(body), "multiple errors encountered")
+			assert.Contains(t, string(body), "parameter \"id\"")
+			assert.Contains(t, string(body), "value abc: an invalid integer: invalid syntax")
+			assert.Contains(t, string(body), "parameter \"id2\"")
+			assert.Contains(t, string(body), "number must be at least 10")
+		}
+		assert.False(t, called, "Handler should not have been called")
+		called = false
+	}
+}
+
+func TestOapiRequestValidatorWithOptionsMultiErrorAndCustomHandler(t *testing.T) {
+	swagger, err := openapi3.NewLoader().LoadFromData(testSchema)
+	require.NoError(t, err, "Error initializing swagger")
+
+	app := fiber.New()
+
+	// Set up an authenticator to check authenticated function. It will allow
+	// access to "someScope", but disallow others.
+	options := Options{
+		Options: openapi3filter.Options{
+			ExcludeRequestBody:    false,
+			ExcludeResponseBody:   false,
+			IncludeResponseStatus: true,
+			MultiError:            true,
+		},
+		MultiErrorHandler: func(me openapi3.MultiError) error {
+			return fmt.Errorf("Bad stuff -  %s", me.Error())
+		},
+	}
+
+	// register middleware
+	app.Use(OapiRequestValidatorWithOptions(swagger, &options))
+
+	called := false
+
+	// Install a request handler for /resource. We want to make sure it doesn't
+	// get called.
+	app.Get("/multiparamresource", func(c *fiber.Ctx) error {
+		called = true
+		return nil
+	})
+
+	// Let's send a good request, it should pass
+	{
+		res := doGet(t, app, "https://deepmap.ai/multiparamresource?id=50&id2=50")
+		assert.Equal(t, http.StatusOK, res.StatusCode)
+		assert.True(t, called, "Handler should have been called")
+		called = false
+	}
+
+	// Let's send a request with a missing parameter, it should return
+	// a bad status
+	{
+		res := doGet(t, app, "https://deepmap.ai/multiparamresource?id=50")
+		assert.Equal(t, http.StatusBadRequest, res.StatusCode)
+		body, err := io.ReadAll(res.Body)
+		if assert.NoError(t, err) {
+			assert.Contains(t, string(body), "Bad stuff")
+			assert.Contains(t, string(body), "parameter \"id2\"")
+			assert.Contains(t, string(body), "value is required but missing")
+		}
+		assert.False(t, called, "Handler should not have been called")
+		called = false
+	}
+
+	// Let's send a request with a 2 missing parameters, it should return
+	// a bad status
+	{
+		res := doGet(t, app, "https://deepmap.ai/multiparamresource")
+		assert.Equal(t, http.StatusBadRequest, res.StatusCode)
+		body, err := io.ReadAll(res.Body)
+		if assert.NoError(t, err) {
+			assert.Contains(t, string(body), "Bad stuff")
+			assert.Contains(t, string(body), "parameter \"id\"")
+			assert.Contains(t, string(body), "value is required but missing")
+			assert.Contains(t, string(body), "parameter \"id2\"")
+			assert.Contains(t, string(body), "value is required but missing")
+		}
+		assert.False(t, called, "Handler should not have been called")
+		called = false
+	}
+
+	// Let's send a request with a 1 missing parameter, and another outside
+	// or the parameters. It should return a bad status
+	{
+		res := doGet(t, app, "https://deepmap.ai/multiparamresource?id=500")
+		assert.Equal(t, http.StatusBadRequest, res.StatusCode)
+		body, err := io.ReadAll(res.Body)
+		if assert.NoError(t, err) {
+			assert.Contains(t, string(body), "Bad stuff")
+			assert.Contains(t, string(body), "parameter \"id\"")
+			assert.Contains(t, string(body), "number must be at most 100")
+			assert.Contains(t, string(body), "parameter \"id2\"")
+			assert.Contains(t, string(body), "value is required but missing")
+		}
+		assert.False(t, called, "Handler should not have been called")
+		called = false
+	}
+
+	// Let's send a request with a parameters that do not meet spec. It should
+	// return a bad status
+	{
+		res := doGet(t, app, "https://deepmap.ai/multiparamresource?id=abc&id2=1")
+		assert.Equal(t, http.StatusBadRequest, res.StatusCode)
+		body, err := io.ReadAll(res.Body)
+		if assert.NoError(t, err) {
+			assert.Contains(t, string(body), "Bad stuff")
+			assert.Contains(t, string(body), "parameter \"id\"")
+			assert.Contains(t, string(body), "value abc: an invalid integer: invalid syntax")
+			assert.Contains(t, string(body), "parameter \"id2\"")
+			assert.Contains(t, string(body), "number must be at least 10")
+		}
+		assert.False(t, called, "Handler should not have been called")
+		called = false
+	}
+}
diff --git a/pkg/fiber-middleware/test_spec.yaml b/pkg/fiber-middleware/test_spec.yaml
new file mode 100644
index 0000000..6e0a241
--- /dev/null
+++ b/pkg/fiber-middleware/test_spec.yaml
@@ -0,0 +1,103 @@
+openapi: "3.0.0"
+info:
+  version: 1.0.0
+  title: TestServer
+servers:
+  - url: http://deepmap.ai/
+paths:
+  /resource:
+    get:
+      operationId: getResource
+      parameters:
+        - name: id
+          in: query
+          schema:
+            type: integer
+            minimum: 10
+            maximum: 100
+      responses:
+        '200':
+          description: success
+          content:
+            application/json:
+              schema:
+                properties:
+                  name:
+                    type: string
+                  id:
+                    type: integer
+    post:
+      operationId: createResource
+      responses:
+        '204':
+          description: No content
+      requestBody:
+        required: true
+        content:
+          application/json:
+            schema:
+              properties:
+                name:
+                  type: string
+  /protected_resource:
+    get:
+      operationId: getProtectedResource
+      security:
+        - BearerAuth:
+            - someScope
+      responses:
+        '204':
+          description: no content
+  /protected_resource2:
+    get:
+      operationId: getProtectedResource
+      security:
+        - BearerAuth:
+            - otherScope
+      responses:
+        '204':
+          description: no content
+  /protected_resource_401:
+    get:
+      operationId: getProtectedResource
+      security:
+        - BearerAuth:
+            - unauthorized
+      responses:
+        '401':
+          description: no content
+  /multiparamresource:
+    get:
+      operationId: getResource
+      parameters:
+        - name: id
+          in: query
+          required: true
+          schema:
+            type: integer
+            minimum: 10
+            maximum: 100
+        - name: id2
+          required: true
+          in: query
+          schema:
+            type: integer
+            minimum: 10
+            maximum: 100
+      responses:
+        '200':
+          description: success
+          content:
+            application/json:
+              schema:
+                properties:
+                  name:
+                    type: string
+                  id:
+                    type: integer
+components:
+  securitySchemes:
+    BearerAuth:
+      type: http
+      scheme: bearer
+      bearerFormat: JWT
diff --git a/pkg/runtime/deepobject_test.go b/pkg/runtime/deepobject_test.go
index d1a5a16..237673a 100644
--- a/pkg/runtime/deepobject_test.go
+++ b/pkg/runtime/deepobject_test.go
@@ -17,18 +17,20 @@ type InnerObject struct {
 
 // These are all possible field types, mandatory and optional.
 type AllFields struct {
-	I   int          `json:"i"`
-	Oi  *int         `json:"oi,omitempty"`
-	F   float32      `json:"f"`
-	Of  *float32     `json:"of,omitempty"`
-	B   bool         `json:"b"`
-	Ob  *bool        `json:"ob,omitempty"`
-	As  []string     `json:"as"`
-	Oas *[]string    `json:"oas,omitempty"`
-	O   InnerObject  `json:"o"`
-	Oo  *InnerObject `json:"oo,omitempty"`
-	D   MockBinder   `json:"d"`
-	Od  *MockBinder  `json:"od,omitempty"`
+	I   int             `json:"i"`
+	Oi  *int            `json:"oi,omitempty"`
+	F   float32         `json:"f"`
+	Of  *float32        `json:"of,omitempty"`
+	B   bool            `json:"b"`
+	Ob  *bool           `json:"ob,omitempty"`
+	As  []string        `json:"as"`
+	Oas *[]string       `json:"oas,omitempty"`
+	O   InnerObject     `json:"o"`
+	Oo  *InnerObject    `json:"oo,omitempty"`
+	D   MockBinder      `json:"d"`
+	Od  *MockBinder     `json:"od,omitempty"`
+	M   map[string]int  `json:"m"`
+	Om  *map[string]int `json:"om,omitempty"`
 }
 
 func TestDeepObject(t *testing.T) {
@@ -40,6 +42,9 @@ func TestDeepObject(t *testing.T) {
 		Name: "Marcin Romaszewicz",
 		ID:   123,
 	}
+	om := map[string]int{
+		"additional": 1,
+	}
 	d := MockBinder{Time: time.Date(2020, 2, 1, 0, 0, 0, 0, time.UTC)}
 
 	srcObj := AllFields{
@@ -58,6 +63,8 @@ func TestDeepObject(t *testing.T) {
 		Oo: &oo,
 		D:  d,
 		Od: &d,
+		M:  om,
+		Om: &om,
 	}
 
 	marshaled, err := MarshalDeepObject(srcObj, "p")