hey-api / openapi-ts

✨ Turn your OpenAPI specification into a beautiful TypeScript client

Home Page:https://heyapi.vercel.app

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Default content-type of application/json applied to requests even when no body

frdwhite24 opened this issue · comments

Description

Thanks for all the hard work on this library, it's an incredible feat and has saved me so much time in my personal projects. I make sure to recommend it to everyone who has a similar problem. Also loving the type safety and flexibility of the latest lib version! I've not felt restricted when used it at all.

I've recently upgraded to the latest version of your lib (0.46.0), and have switched from using axios as the client to @hey-api/client-fetch (0.1.3) because I needed to add custom headers in an interceptor. Previously all requests worked fine, but after carrying out the appropriate refactoring for the new library version and config format, the requests without a body are failing with a FastifyError from my backend of:

Body cannot be empty when content-type is set to 'application/json'

Inspecting the requests shows there is indeed a header with content-type: "application/json" being applied. If I write the following code in a request interceptor, then the requests (namely POST and DELETE) go through just fine as before. It seems there is a default content-type of application/json being applied?

client.interceptors.request.use(async (request) => {
  const token = await getToken()
  request.headers.set('Authorization', `Bearer ${token}`)

  if (some condition here) {
    request.headers.delete('content-type')

  return request
})

I'm not sure of the best way to handle this, but feel free to ask any more questions if you need to so we can figure it out together.

OpenAPI specification (optional)

{
  "openapi":"3.0.3",
  "info":{
    "title":"MyApp",
    "description":"REST API for MyApp",
    "version":"1.0.0"
  },
  "components":{
    
  },
  "paths":{
    "/api/flights/{flightId}":{
      "delete":{
        "operationId":"deleteOneFlight",
        "tags":[
          "Flight"
        ],
        "parameters":[
          {
            "schema":{
              "type":"string"
            },
            "in":"path",
            "name":"flightId",
            "required":true
          }
        ],
        "responses":{
          "204":{
            "description":"Default Response",
            "content":{
              "application/json":{
                "schema":{
                  "type":"object",
                  "properties":{
                    
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}

Configuration

// openapi-ts.config.ts
import { defineConfig } from '@hey-api/openapi-ts'

export default defineConfig({
  client: '@hey-api/client-fetch',
  input: 'openapi.json',
  output: {
    format: 'prettier',
    path: 'src/api/generated'
  },
  types: {
    enums: 'javascript',
  },
})

System information (optional)

  • MacOS: Sonoma v14.5
  • node: v20.12.2
  • npm: v10.5.0
  • @hey-api/client-fetch: v0.1.3 <- previously axios v1.6.8
  • @hey-api/openapi-ts: v0.46.3 <- previously v0.43.0
  • react-native: v0.73.6
  • expo: v50.0.19

The same thing seems to happen with a POST request that doesn't have any body as well, have updated the original issue text/title and solution

For anyone else experiencing this, you can use the temporary workaround below. I think this is definitely something that should be addressed with the new @hey-api/client-fetch lib. I've had to go through all my endpoints and manually pick out the ones that: are not GET, and don't send a body, and remove their content-type header.

client.interceptors.request.use(async (request) => {
  const method = request.method
  if (method === 'DELETE' || method === 'POST') { // or any other methods where you're sending something without a body (GET isn't required)
    ENDPOINTS_TO_REMOVE[method].forEach((endpoint) => {
      if (endpoint.regex.test(request.url)) {
        request.headers.delete('content-type')
      }
    })
  }
  return request
})

where:

export const ENDPOINTS_TO_REMOVE = {
  DELETE: [
    {
      url: '/api/my-endpoint',
      regex: /\/api\/my-endpoint/, // using regex just in case you have url parameters
    }
]

Probably, if you use @hey-api/client-fetch there is simpler workaround:

client.interceptors.request.use((request, options) => {
  // ... 
  if(!options.body) request.headers.delete('content-type')
  return request
});

I faced this problem in .NET 8 backend. I send simple get request, but if content-type header specified, .NET Model binding system tries to get data from body and i get binding error. Even if i explicitly specify to bind model from query.

In my opinion content-type header should be removed in all request methods of clients packages if options.body is null or undefined (at least in get method).

I wouldn't be opposed to doing that @nrmncr