简体   繁体   中英

go rest api server design good practice

I am new to golang and would like to make a small web app of to-do list to polish my go skills. I would like to know what is a good practice to organize the code.

Right now, I define two packages for this project: ticket and server . ticket is about database and server is about http handlers.

My data has two types: Ticket and Todo. One Ticket can have multiple Todo in it. They are defined in the ticket package.

type Ticket struct {                                                               
    Id          int64      `db:"id" json:"id"`                                     
    Label       string     `db:"label" json:"label"`                               
    Description string     `db:"description" json:"description"`                   
    StartTime   time.Time  `db:"start_time" json:"start_time"`                     
    EndTime     *time.Time `db:"end_time" json:"end_time"`                         
    Priority    bool       `db:"priority" json:"priority"`                         
}                                                                                  
type Todo struct {                                                                 
    Id       int64 `db:"id" json:"id"`                                             
    Item     int64 `db:"item" json:"item"`                                         
    TicketId int64 `db:"ticket_id" json:"ticket_id"`                               
    Active   bool  `db:"active" json:"active"`                                     
}  

In the ticket package, I also define

type AppDB struct {                                                                
    db *sqlx.DB                                                                    
}   

This wrapping around *sqlx.DB allows me to put all the database access functions inside ticket package, eg,

func (adb *AppDB) MustInit() 
func (adb *AppDB) AddTicket(i *Ticket) (int64, error)

In the server package, I define

type Application struct {                                                          
    db ticket.AppDB                                                                
}   

And the http handler functions are defined as methods of Application , eg,

func (app *Application) CreateTicket(w http.ResponseWriter, req *http.Request)

In the main.go , I register the handle functions.

func main() {                                                                      
    app := server.NewApplication()                                                 

    fmt.Println("now listening...")                                                
    router := mux.NewRouter()                                                      
    router.HandleFunc("/", app.Hello).Methods("GET")                               
    router.HandleFunc("/get", app.Get).Methods("GET")                              
    log.Fatal(http.ListenAndServe(":"+os.Getenv("PORT"), router))                  
}  

I have the following questions:

  1. Is this design a good practice? If not, what is the right way?
  2. In Donovan and Kernighan's Go programming book p.194, they give an example where the http handlers are defined as the database's method directly. Would that be a better design? It makes sense because my server package only works for data types in ticket . It also makes the code a little cleaner to write. On the other hand it mixes http and database, which I am not sure if it is a good thing.
  3. How to deal with database table creation? Shall I create another main.go which only creates the database tables and build it into executable such that I can run it on the server just once?
  1. I think making 2 packages is a bad decision based on what you've described. If you're making a "small web app", there's nothing to gain but extra complexity in having more than one package. If you're not sure, always choose the simpler solution.

  2. I don't think having 2 packages is cleaner or that it mixes less. You can have your package stuff on several different files, and functions as methods of different structs.

  3. How about this? Define your db initialisation in an idempotent way (eg CREATE SCHEMA IF NOT EXISTS, CREATE TABLE IF NOT EXISTS) and run it every single time at the beginning of your main.go , right after you db.Ping() . Should be milliseconds most of the time, and you ensure that the db always has the structure you expect.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM