In the succeeding entries, I will use OpenAPI Generator tool to generate default route and controller. This allows me to focus on implementation and less on handling transport layer configuration (router and controller). A router creates a mux router, and a controller aligns paths with their respective handlers.
Example output from openapi-generator-cli generate -c openapi/config/go-server.yaml
── server
│ ├── gen
│ │ ├── Dockerfile
│ │ ├── README.md
│ │ ├── api
│ │ │ └── openapi.yaml
│ │ ├── go
│ │ │ ├── api.go
│ │ │ ├── api_default.go
│ │ │ ├── api_default_service.go
│ │ │ ├── error.go
│ │ │ ├── helpers.go
│ │ │ ├── impl.go
│ │ │ ├── logger.go
│ │ │ ├── model_journal_entry.go
│ │ │ ├── model_journal_entry_create.go
│ │ │ └── routers.go
│ │ ├── go.mod
│ │ └── main.go
Generated main.go looks like this:
func main() {
log.Printf("Server started")
DefaultAPIService := gen.NewDefaultAPIService()
DefaultAPIController := gen.NewDefaultAPIController(DefaultAPIService)
router := gen.NewRouter(DefaultAPIController)
log.Fatal(http.ListenAndServe(":8080", router))
}
DefaultAPIService is the controller’s service implementation; it is invoked by the controller when /journals is requested. Note that this is different from a typical domain service, which should not contain transport layer specifics (in the example, it returns an HTTP code, so this is not considered a domain service).
func NewDefaultAPIService() *DefaultAPIService {
return &DefaultAPIService{}
}
// CreateJournal - Create a journal
func (s *DefaultAPIService) CreateJournal(ctx context.Context, journalEntryCreate JournalEntryCreate) (ImplResponse, error) {
return Response(http.StatusNotImplemented, nil), errors.New("CreateJournal method not implemented")
}
The controller wires up the handler to its route.
func NewDefaultAPIController(s DefaultAPIServicer, opts ...DefaultAPIOption) *DefaultAPIController {
controller := &DefaultAPIController{
service: s,
errorHandler: DefaultErrorHandler,
}
for _, opt := range opts {
opt(controller)
}
return controller
}
// Routes returns all the api routes for the DefaultAPIController
func (c *DefaultAPIController) Routes() Routes {
return Routes{
"CreateJournal": Route{
"CreateJournal",
strings.ToUpper("Post"),
"/journals",
c.CreateJournal,
},
}
}
Finally, this creates a mux router with the route definitions from above.
func NewRouter(routers ...Router) *mux.Router {
router := mux.NewRouter().StrictSlash(true)
for _, api := range routers {
for _, route := range api.OrderedRoutes() {
var handler http.Handler = route.HandlerFunc
handler = Logger(handler, route.Name)
router.
Methods(route.Method).
Path(route.Pattern).
Name(route.Name).
Handler(handler)
}
}
return router
}