Support Enum as Separate Type in OpenAPI Spec for Client Generators
Is there a way to add enums as separate types in the OpenAPI specification file, rather than using inline enum definitions. This will allow client generators (e.g., TypeScript/JavaScript) to create reusable enum types that can be used by applications like Angular, React, etc.
how "enums" are generated right now:
"ObjectFoo": {
"properties": {
"fooType": {
"enum": [
"ENUM_Value_ONE",
"ENUM_Value_TWO",
"ENUM_Value_THREE"
],
"example": "ENUM_Value_ONE",
"type": "string"
}
}
}
how "enums" could be generated as extra types:
components:
schemas:
FooTypeEnum:
type: string
enum:
- ENUM_Value_ONE
- ENUM_Value_TWO
- ENUM_Value_THREE
"ObjectFoo": {
"properties": {
"fooType": {
"$ref": "#/components/schemas/FooTypeEnum"
}
}
}
@neffsvg +1
It's kinda related to this one https://github.com/danielgtaylor/huma/issues/560
I'm going to see if I can implement this
In case that helps, here is a workaround that relies on implementing the SchemaProvider interface for enums
type InstitutionKind string
const (
Lab InstitutionKind = "Lab"
FoundingAgency InstitutionKind = "FundingAgency"
SequencingPlatform InstitutionKind = "SequencingPlatform"
Other InstitutionKind = "Other"
)
var InstitutionKindValues = []InstitutionKind{
Lab,
FoundingAgency,
SequencingPlatform,
Other,
}
// Register enum in OpenAPI specification
func (u InstitutionKind) Schema(r huma.Registry) *huma.Schema {
if r.Map()["InstitutionKind"] == nil {
schemaRef := r.Schema(reflect.TypeOf(""), true, "InstitutionKind")
schemaRef.Title = "InstitutionKind"
for _, v := range InstitutionKindValues {
schemaRef.Enum = append(schemaRef.Enum, string(v))
}
r.Map()["InstitutionKind"] = schemaRef
}
return &huma.Schema{Ref: "#/components/schemas/InstitutionKind"}
}
I use code generation to keep the enum values array up to date and avoid typing boilerplate for every enum type.
Thanks @lsdch - this is super useful.
If anyone else is using @lsdch solution above, it's likely that the InstitutionKindValues might be a map (especially if your enum is generated).
If this is the case, you can write a simple function to sort the map:
import "sort"
func SortMap[T any](m map[string]T) []string {
keys := make([]string, 0, len(m))
for k := range m {
keys = append(keys, k)
}
sort.Strings(keys)
return keys
}
// Register enum in OpenAPI specification
func (u InstitutionKind) Schema(r huma.Registry) *huma.Schema {
if r.Map()["InstitutionKind"] == nil {
schemaRef := r.Schema(reflect.TypeOf(""), true, "InstitutionKind")
schemaRef.Title = "InstitutionKind"
for _, v := range SortMap(InstitutionKindValues) {
schemaRef.Enum = append(schemaRef.Enum, string(v))
}
r.Map()["InstitutionKind"] = schemaRef
}
return &huma.Schema{Ref: "#/components/schemas/InstitutionKind"}
}
This will stop the openapi.yaml diff from changing each time.