Skip to content

OpenAPI spec generated for server sent events contains null which potentially doesn't follow the OpenAPI 3.1 spec #207

@JanRuettinger

Description

@JanRuettinger

The sse-test endpoint below generates an OpenAPI spec that contains a null type for each of the three output events.
These null types cause issues downstream when generating clients with e.g. orval.

Error when working with the generated OpenAPI spec:
TypeError: Cannot read properties of null (reading 'nullable')

There is also a discussion in the official OpenAPI spec repo regarding that issue. Link Apparently there are different versions on how to define a null type. type: 'null' and just 'null'. Right now huma generates the type as null without quotes. According to this migration guide from OpenAPI 3.0 to 3.1, null types should be represented as "type": "null"

I am not sure if the issue needs to be fixed on the client generating side, i.e. they need to deal with null better. Or that huma should represent null differently when generating the OpenAPI spec, e.g. "type": "null".

Below is the code and the generated OpenAPI spec.

Happy to submit a PR with some guidance!

sse-test endpoint

type DefaultMessage struct {
	Message string `json:"message"`
}

type UserCreatedEvent struct {
	UserID string `json:"userID"`
}

type MailReceivedEvent struct {
	UserID string `json:"userID"`
}

func (app *application) RegisterChatTest(api huma.API) {
	sse.Register(api, huma.Operation{
		OperationID: "sse",
		Method:      http.MethodGet,
		Path:        "/sse-test",
		Summary:     "Server sent events example",
	}, map[string]any{
		// Mapping of event type name to Go struct for that event.
		"message":      DefaultMessage{},
		"userCreate":   UserCreatedEvent{},
		"mailRecieved": MailReceivedEvent{},
	}, func(ctx context.Context, input *struct{}, send sse.Sender) {
		// Send an event every second for 10 seconds.
		for x := 0; x < 10; x++ {
			send.Data(MailReceivedEvent{UserID: "abc123"})
			time.Sleep(1 * time.Second)
		}
	})
}

Generated OpenAPI spec

"/sse-test": {
            "get": {
                "summary": "Server sent events example",
                "operationId": "sse",
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "text/event-stream": {
                                "schema": {
                                    "type": "array",
                                    "title": "Server Sent Events",
                                    "description": "Each oneOf object in the array represents one possible Server Sent Events (SSE) message, serialized as UTF-8 text according to the SSE specification.",
                                    "items": {
                                        "oneOf": [
                                            null,
                                            null,
                                            null,
                                            {
                                                "type": "object",
                                                "title": "Event message",
                                                "properties": {
                                                    "data": { "$ref": "#/components/schemas/DefaultMessage" },
                                                    "event": {
                                                        "type": "string",
                                                        "description": "The event name.",
                                                        "const": "message"
                                                    },
                                                    "id": { "type": "integer", "description": "The event ID." },
                                                    "retry": {
                                                        "type": "integer",
                                                        "description": "The retry time in milliseconds."
                                                    }
                                                },
                                                "required": ["data"]
                                            },
                                            {
                                                "type": "object",
                                                "title": "Event userCreate",
                                                "properties": {
                                                    "data": { "$ref": "#/components/schemas/UserCreatedEvent" },
                                                    "event": {
                                                        "type": "string",
                                                        "description": "The event name.",
                                                        "const": "userCreate"
                                                    },
                                                    "id": { "type": "integer", "description": "The event ID." },
                                                    "retry": {
                                                        "type": "integer",
                                                        "description": "The retry time in milliseconds."
                                                    }
                                                },
                                                "required": ["data", "event"]
                                            },
                                            {
                                                "type": "object",
                                                "title": "Event mailRecieved",
                                                "properties": {
                                                    "data": { "$ref": "#/components/schemas/MailReceivedEvent" },
                                                    "event": {
                                                        "type": "string",
                                                        "description": "The event name.",
                                                        "const": "mailRecieved"
                                                    },
                                                    "id": { "type": "integer", "description": "The event ID." },
                                                    "retry": {
                                                        "type": "integer",
                                                        "description": "The retry time in milliseconds."
                                                    }
                                                },
                                                "required": ["data", "event"]
                                            }
                                        ]
                                    }
                                }
                            }
                        }
                    },
                    "default": {
                        "description": "Error",
                        "content": {
                            "application/problem+json": { "schema": { "$ref": "#/components/schemas/ErrorModel" } }
                        }
                    }
                }
            }
        },

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions