Request body not set in DELETE handlers via huma OpenAPI UI
First of all thank you a lot for this awesome framework! Love it! ❤️
I guess i found a little issue in the OpenAPI documentation UI.
Problem
When issuing a request to a delete handler, that takes a request body, via Huma OpenAPI UI, the request fails with the following error. The curl statement shows that the request will not include the request body.
{
"$schema": "http://localhost:8888/schemas/ErrorModel.json",
"title": "Bad Request",
"status": 400,
"detail": "request body is required"
}
When defining a handler with http.MethodDelete i want to be able to pass a request body to the handler via OpenAPI UI.
Expected Behavior
Whenever a request body passes the validation i want the handler to be able to read the request body and process it. Whenever the validation of the request body fails i want to receive a 422.
Reproduce
This minimal example demonstrates the how to reproduce the error. It was tested with versions v2.26.0, v2.28.0, v2.30.0, v2.32.0, v2.33.0, v2.34.1.
package main
import (
"context"
"log/slog"
"net/http"
"github.com/danielgtaylor/huma/v2"
"github.com/danielgtaylor/huma/v2/adapters/humachi"
"github.com/go-chi/chi/v5"
)
type UserDeleteInput struct {
Body UserDeleteInputBody `body:""`
}
type UserDeleteInputBody struct {
UserIDs []string `json:"user_ids" doc:"The IDs of the users to delete" minItems:"1"`
}
type DeleteResponse struct {
Success bool `json:"success"`
}
func main() {
router := gin.Default()
api := humagin.New(router, huma.DefaultConfig("My API", "1.0.0"))
huma.Register(api, huma.Operation{
Method: http.MethodDelete,
Path: "/api/companies/users",
OperationID: "delete-users-v1",
Summary: "Deletes a list of users (v1)",
Description: "This endpoint deletes a list of users. It is authenticated and requires a valid access token.",
Tags: []string{"users"},
}, UserDeleteHandler)
http.ListenAndServe(":8888", router)
}
func UserDeleteHandler(ctx context.Context, input *UserDeleteInput) (*DeleteResponse, error) {
users := input.Body.UserIDs
slog.Info("Users received for deletion", "users", users)
out := &DeleteResponse{
Success: true,
}
slog.Info("success")
return out, nil
}
Run the server.
go run main.go
Open a browser and head to http://localhost:8888/docs#/operations/delete-users-v1. Send a request via the OpenAPI UI. The request will fail with the error:
{
"$schema": "http://localhost:8888/schemas/ErrorModel.json",
"title": "Bad Request",
"status": 400,
"detail": "request body is required"
}
Send requests to the server via curl. The request succeeds.
curl -X DELETE http://localhost:8888/api/companies/users -H "Content-Type: application/json" -d '{"user_ids": ["123-456"]}' -v
* Host localhost:8888 was resolved.
* IPv6: ::1
* IPv4: 127.0.0.1
* Trying [::1]:8888...
* Connected to localhost (::1) port 8888
> DELETE /api/companies/users HTTP/1.1
> Host: localhost:8888
> User-Agent: curl/8.7.1
> Accept: */*
> Content-Type: application/json
> Content-Length: 25
>
* upload completely sent off: 25 bytes
< HTTP/1.1 204 No Content
< Success: true
< Date: Wed, 27 Aug 2025 09:35:26 GMT
<
* Connection #0 to host localhost left intact
I'd be interested to have a first look at this.
+1 for this framework. It has really shaped the way we communicate our APIs in the organization. Thank you ❤️
Huma doesn't have any UI natively. Under the hood, it uses Stoplight Elements and i have created a merge request there already
Same issue, is there a workaround until @sjayach MR is merged and a new version is released ?
@MathieuCesbron Yes. It's possible
Clone or Download https://github.com/sjayach/elements/blob/add-delete-body and run below commands
yarn install
yarn run build
Copy below files from elements-add-delete-body/packages/elements/dist to your golang codebase under stoplight directory
index.esm.js
index.js
index.mjs
styles.min.css
web-components.min.js
And static directory hosting as part of your own router. ( below code uses mux router)
staticDir := "stoplight/" // or wherever your static files are located
r.PathPrefix("/stoplight/").Handler(http.StripPrefix("/stoplight/", http.FileServer(http.Dir(staticDir)))).Methods(http.MethodGet)
r.HandleFunc("/docs", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/html")
w.Write([]byte(`<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="referrer" content="same-origin" />
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<title>Docs Example reference</title>
<!-- Embed elements Elements via Web Component -->
<link href="https://unpkg.com/@stoplight/[email protected]/styles.min.css" rel="stylesheet" />
<script src="stoplight/web-components.min.js"
crossorigin="anonymous"></script>
</head>
<body style="height: 100vh;">
<elements-api
apiDescriptionUrl="/openapi.yaml"
router="hash"
layout="sidebar"
tryItCredentialsPolicy="same-origin"
/>
</body>
</html>`))
}).Methods(http.MethodGet)
this will use local stoplight with above MR changes.