# Beego File structure [Api Based App] ![](https://i.imgur.com/yYEL7GW.png) ## Folders & Files - conf - ini file,key value pair - holds configuration of application - maps with web.Config - example : httpport = 8080 - controller - controller for a specific namespace - inherits web.Controller - overrides methods from web.Controller - Get, Post..... - models - holds model for each entity - has database logic - interactions with database - routers - routers are defined - namespaces are defined and controllers are assigned - has init() functions, which loads at the initial state of running the application - tests - includes http tests/other tests - other files - main.go - go mod and sum - .gitignore - ...... - swagger/swagger.zip - depending how the application is started all documentation can be downloaded as `swagger.zip` and extracted to `swagger` folder ## Examples/Details ### conf conf folder has a file named `app.conf` which is an `ini` file ```shell= appname = beegotodo httpport = 8080 runmode = dev autorender = false copyrequestbody = true EnableDocs = true sqlconn = ``` ### controller controller folder has all the controllers, lets take an example of todocontroller ```go= // TodoController operations for Todo type TodoController struct { beego.Controller } // URLMapping ... func (c *TodoController) URLMapping() { c.Mapping("Post", c.Post) c.Mapping("GetOne", c.GetOne) c.Mapping("GetAll", c.GetAll) c.Mapping("Put", c.Put) c.Mapping("Delete", c.Delete) } // Post ... // @Title Post // @Description create Todo // @Param body body models.Todo true "body for Todo content" // @Success 201 {int} models.Todo // @Failure 403 body is empty // @router / [post] func (c *TodoController) Post() { var v models.Todo json.Unmarshal(c.Ctx.Input.RequestBody, &v) if _, err := models.AddTodo(&v); err == nil { c.Ctx.Output.SetStatus(201) c.Data["json"] = v } else { c.Data["json"] = err.Error() } c.ServeJSON() } ``` - inherits from web.Controller - overrides methods - `web.Controller` implements `ControllerInterface` - `UrlMapping`, maps each http method to a function - `Post` handles the post method request - Controllers communicate with models and serves data from getting it from models or receives data and passes it to models - Can you see some comments above of the `Post()` method ? - these comments are route, method, request, response models,params defination - these comments are used to make useful Api documentation - also generates global controller route inside `route` package ### models All models for application goes here, as well as the database CRUD functionalities. lets take an example of todo model ```go= package models import ( "errors" "fmt" "github.com/beego/beego/v2/client/orm" ) var ( todos = []Todo{ { Id: 1, Title: "task 1", Completed: false, }, { Id: 2, Title: "task 2", Completed: false, }, { Id: 3, Title: "task 3", Completed: true, }, } ) type Todo struct { Id int64 `orm:"auto"` Title string `orm:"size(128)"` Completed bool } func init() { orm.RegisterModel(new(Todo)) } // AddTodo insert a new Todo into database and returns // last inserted Id on success. func AddTodo(m *Todo) (id int64, err error) { todos = append(todos, *m) return int64(len(todos)), nil } ``` - Define models with tagging - Register the model - in this particular example we are using slice of todo, but real life scenario would be crud interactions with database, such as mysql,postgres.... ### routers router package is where the namespaces are defined, as well as respected controllers ```go= // @APIVersion 1.0.0 // @Title beego Test API // @Description beego has a very cool tools to autogenerate documents for your API // @Contact astaxie@gmail.com // @TermsOfServiceUrl http://beego.me/ // @License Apache 2.0 // @LicenseUrl http://www.apache.org/licenses/LICENSE-2.0.html package routers import ( "github.com/nafeem-evatix/beegotodo/controllers" beego "github.com/beego/beego/v2/server/web" ) func init() { ns := beego.NewNamespace("/v1", beego.NSNamespace("/todo", beego.NSInclude( &controllers.TodoController{}, ), ), ) beego.AddNamespace(ns) } ``` - notice the comments before `package routers` this also part of the API documentation and is added to the swagger page - in the init function new namespace is created also todo namespace is defined - and then added to the beego namespaces - in simple words this block of code defines API version, creates `todo` name space and mentions which controller should take care of incoming request that starts with `/todo` - another file `commentsRouter_controllers.go` is generated when running `bee run` ```go= package routers import ( beego "github.com/beego/beego/v2/server/web" "github.com/beego/beego/v2/server/web/context/param" ) func init() { beego.GlobalControllerRouter["github.com/nafeem-evatix/beegotodo/controllers:TodoController"] = append(beego.GlobalControllerRouter["github.com/nafeem-evatix/beegotodo/controllers:TodoController"], beego.ControllerComments{ Method: "Post", Router: "/", AllowHTTPMethods: []string{"post"}, MethodParams: param.Make(), Filters: nil, Params: nil}) beego.GlobalControllerRouter["github.com/nafeem-evatix/beegotodo/controllers:TodoController"] = append(beego.GlobalControllerRouter["github.com/nafeem-evatix/beegotodo/controllers:TodoController"], beego.ControllerComments{ Method: "GetAll", Router: "/", AllowHTTPMethods: []string{"get"}, MethodParams: param.Make(), Filters: nil, Params: nil}) beego.GlobalControllerRouter["github.com/nafeem-evatix/beegotodo/controllers:TodoController"] = append(beego.GlobalControllerRouter["github.com/nafeem-evatix/beegotodo/controllers:TodoController"], beego.ControllerComments{ Method: "GetOne", Router: "/:id", AllowHTTPMethods: []string{"get"}, MethodParams: param.Make(), Filters: nil, Params: nil}) beego.GlobalControllerRouter["github.com/nafeem-evatix/beegotodo/controllers:TodoController"] = append(beego.GlobalControllerRouter["github.com/nafeem-evatix/beegotodo/controllers:TodoController"], beego.ControllerComments{ Method: "Put", Router: "/:id", AllowHTTPMethods: []string{"put"}, MethodParams: param.Make(), Filters: nil, Params: nil}) beego.GlobalControllerRouter["github.com/nafeem-evatix/beegotodo/controllers:TodoController"] = append(beego.GlobalControllerRouter["github.com/nafeem-evatix/beegotodo/controllers:TodoController"], beego.ControllerComments{ Method: "Delete", Router: "/:id", AllowHTTPMethods: []string{"delete"}, MethodParams: param.Make(), Filters: nil, Params: nil}) } ``` - this file is generated from the comments defined in the controller package - this is a map, which uses controller location as key and uses slice of CommentControllers of which each block contains information about the Method to execute for which URL ### tests all the http tests and other tests should go in this package, a sample test is provided when generating project using `bee` tool. ```go= package test import ( "net/http" "net/http/httptest" "testing" "runtime" "path/filepath" _ "github.com/nafeem-evatix/beegotodo/routers" beego "github.com/beego/beego/v2/server/web" "github.com/beego/beego/v2/core/logs" . "github.com/smartystreets/goconvey/convey" ) func init() { _, file, _, _ := runtime.Caller(0) apppath, _ := filepath.Abs(filepath.Dir(filepath.Join(file, ".." + string(filepath.Separator)))) beego.TestBeegoInit(apppath) } // TestGet is a sample to run an endpoint test func TestGet(t *testing.T) { r, _ := http.NewRequest("GET", "/v1/todo", nil) w := httptest.NewRecorder() beego.BeeApp.Handlers.ServeHTTP(w, r) logs.Info("testing", "TestGet", "Code[%d]\n%s", w.Code, w.Body.String()) Convey("Subject: Test Station Endpoint\n", t, func() { Convey("Status Code Should Be 200", func() { So(w.Code, ShouldEqual, 200) }) Convey("The Result Should Not Be Empty", func() { So(w.Body.Len(), ShouldBeGreaterThan, 0) }) }) } ``` ### swagger/swagger.zip Depending on how the app is ran, docs can be generated, downloaded,extracted and served ### Others main.go, mod, sum, gitignore files.... ## Advantages - Follows `MVC` pattern - Since generated by tool less likely to have errors - The `bee` tool works great with this structure - Less work, generating models, controller usually is based on this file structure and the controllers are added to the controller package, models are added to model package ## Disadvantages - Less changable, as follows `MVC` pattern - Lots of `init()` function, all are initiated when the application is ran - lots of controllers and models can make the file structure messy. - Hard to explain things, for example you can see things like - web.Run(":"), and then what it runs where are the routers ? as previously mentioned, there are lots of init() functions lot of things at a first glance looks like magic ## Changes essential to this structure for cimux - drop models functionalities - we have separate dbapi - can be used to define some models - add service layers instead - to communicate with dbapi