-
-
Notifications
You must be signed in to change notification settings - Fork 165
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
How to separate internal and external API routes? #588
Comments
Hey @cplaetzinger I think, you can achieve it by setting https://pkg.go.dev/github.com/danielgtaylor/huma/v2#Operation
|
Thanks for your input. We're aware of this flag but as it will hide these endpoints completely. We want to generate two api documentations which are based on the same source but contain different endpoints. |
@cplaetzinger, if I understand your idea, this is probably one way to have the only source and two different specs. Pseudo API with four endpoints. In this example, there are:
The diff between yaml files: 5a6,12
> $schema:
> description: A URL to the JSON Schema for this object.
> examples:
> - https://example.com/schemas/CreateUserOutputBody.json
> format: uri
> readOnly: true
> type: string
13a21,27
> $schema:
> description: A URL to the JSON Schema for this object.
> examples:
> - https://example.com/schemas/DeleteUserOutputBody.json
> format: uri
> readOnly: true
> type: string
109c123
< title: Public API
---
> title: Private API
112a127,142
> /user:
> post:
> operationId: CreateUser
> responses:
> "200":
> content:
> application/json:
> schema:
> $ref: "#/components/schemas/CreateUserOutputBody"
> description: OK
> default:
> content:
> application/problem+json:
> schema:
> $ref: "#/components/schemas/ErrorModel"
> description: Error
113a144,164
> delete:
> operationId: DeleteUser
> parameters:
> - in: path
> name: id
> required: true
> schema:
> type: string
> responses:
> "200":
> content:
> application/json:
> schema:
> $ref: "#/components/schemas/DeleteUserOutputBody"
> description: OK
> default:
> content:
> application/problem+json:
> schema:
> $ref: "#/components/schemas/ErrorModel"
> description: Error Example package main
import (
"context"
"net/http"
"os"
"github.com/danielgtaylor/huma/v2"
"github.com/danielgtaylor/huma/v2/adapters/humachi"
"github.com/go-chi/chi/v5"
)
type CreateUserInput struct{}
type CreateUserOutput struct {
Body struct {
Message string `json:"message"`
}
}
type GetUserInput struct {
ID string `path:"id" required:"true"`
}
type GetUserOutput struct {
Body struct {
Message string `json:"message"`
}
}
type UpdateUserInput struct {
ID string `path:"id" required:"true"`
}
type UpdateUserOutput struct {
Body struct {
Message string `json:"message"`
}
}
type DeleteUserInput struct {
ID string `path:"id" required:"true"`
}
type DeleteUserOutput struct {
Body struct {
Message string `json:"message"`
}
}
// Function to add routes with the ability to hide specific operations
func addRoutes(api huma.API, hiddenOperations []string) {
isHidden := func(operationID string) bool {
for _, id := range hiddenOperations {
if id == operationID {
return true
}
}
return false
}
// CreateUser route
huma.Register(api, huma.Operation{
OperationID: "CreateUser",
Method: http.MethodPost,
Path: "/user",
Hidden: isHidden("CreateUser"),
}, func(ctx context.Context, input *CreateUserInput) (*CreateUserOutput, error) {
resp := &CreateUserOutput{}
resp.Body.Message = "CreateUser works!"
return resp, nil
})
// GetUser route
huma.Register(api, huma.Operation{
OperationID: "GetUser",
Method: http.MethodGet,
Path: "/user/{id}",
Hidden: isHidden("GetUser"),
}, func(ctx context.Context, input *GetUserInput) (*GetUserOutput, error) {
resp := &GetUserOutput{}
resp.Body.Message = "GetUser with ID: " + input.ID + " works!"
return resp, nil
})
// UpdateUser route
huma.Register(api, huma.Operation{
OperationID: "UpdateUser",
Method: http.MethodPut,
Path: "/user/{id}",
Hidden: isHidden("UpdateUser"),
}, func(ctx context.Context, input *UpdateUserInput) (*UpdateUserOutput, error) {
resp := &UpdateUserOutput{}
resp.Body.Message = "UpdateUser with ID: " + input.ID + " works!"
return resp, nil
})
// DeleteUser route
huma.Register(api, huma.Operation{
OperationID: "DeleteUser",
Method: http.MethodDelete,
Path: "/user/{id}",
Hidden: isHidden("DeleteUser"),
}, func(ctx context.Context, input *DeleteUserInput) (*DeleteUserOutput, error) {
resp := &DeleteUserOutput{}
resp.Body.Message = "DeleteUser with ID: " + input.ID + " works!"
return resp, nil
})
}
func newAPI(name, version string, hiddenOperations []string) huma.API {
router := chi.NewMux()
cfg := huma.DefaultConfig(name, version)
api := humachi.New(router, cfg)
addRoutes(api, hiddenOperations)
return api
}
func saveAPI(api huma.API, filename string) {
spec, err := api.OpenAPI().YAML()
if err != nil {
panic(err)
}
if err := os.WriteFile(filename, spec, 0644); err != nil {
panic(err)
}
}
func main() {
publicAPI := newAPI("Public API", "1.0.0", []string{"DeleteUser", "CreateUser"})
privateAPI := newAPI("Private API", "1.0.0", []string{})
saveAPI(publicAPI, "public-api.yaml")
saveAPI(privateAPI, "private-api.yaml")
} I hope that makes sense for you. |
Many thanks! I'll try that. |
@cplaetzinger let me know if that works for you. @superstas thanks for the help! BTW in the past I also used something like https://github.com/danielgtaylor/apiscrub and just added some extensions into the OpenAPI to mark which operations were private, then had a separate step to publish both OpenAPI documents. I do like the idea of doing it all in-process and with Huma though! BTW this is a common enough ask I've gotten that I'm open to ideas for how to make this easier. |
I believe we should make it possible without modifying the client code that registers the operations but redirecting the registration calls to the separate instances of OpenAPI instead depending on provided "api" instance. Having "derived groups" concept, we can provide different |
Hi there,
is there a way to generate two different versions of the OpenAPI specification and distinguish between internal and external routes? Our idea is that we want only specific routes to be included in the documentation we provide to some of our clients. Some other routes should not appear there at all because they are only for internal use by us. Any ideas on how this can be archived?
Many thanks
Christian
The text was updated successfully, but these errors were encountered: