# Beego File structure [Api Based App]

## 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