skip to content
Alvin Lucillo

Generated router and controller

/ 2 min read

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
}