Skip to content

coding-standard/golang-gateway-project-layout

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

16 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

golang-project-layout

Standard HTTP and GRPC Go Project Layout with Protobuf and GORM.

Features Included

  • HTTP API
  • GRPC API
  • GORM based MySQL
  • JWT Generation
  • Full Sample Code for Beginners

If you like this project layout, STAR IT NOW!

How to run this project?

  1. You need a MySQL Server, go to schema folder to apply all scripts to your MySQL. (or change the code delete all DAO related code.)

  2. Prepare Protobuf API generation by run make api_dep_install

  3. Generate APIs by run make api_gen

  4. Run this project in GoLand or build it by make build then run make run to start the project. (for different OS and ARCH you may need change GOOS and GOARCH envs, maybe this link can help: https://www.digitalocean.com/community/tutorials/how-to-build-go-executables-for-multiple-platforms-on-ubuntu-16-04)

  5. Visit http://localhost:8081/api/v1/swagger, you can test the APIs.

  6. Notice that only the Token API does not need a token, other APIs need a token to access.

  7. Set token in swagger by click the Authorize button and input the token in the input box, start with bearer, like: bearer your_token_here.

Project Layout

│ .env # if you want to use JWT, store an env like: JWT_SECRET=golang in this file, it will not be submit to git. │ .gitignore │ go.mod │ go.sum │ LICENSE │ Makefile │ README.md # this file │ ├─api # protobuf and api definition │ ├─general │ │ └─v1 │ │ demo.proto │ │ │ └─golang-project-layout │ └─v1 │ golang-project-layout.proto │ README.md │ ├─cmd # project start from here │ └─golang-project-layout │ main.go │ ├─config # config files │ config.yaml │ ├─dist │ └─sdk # the typescript SDK will be generated here │ │ .gitkeep │ ├─docs │ │ .gitkeep │ └─swagger-ui # swagger static files │ ├─internal # internal packages here │ ├─cmd │ │ root.go │ │ server.go │ │ │ ├─dao # database operation │ │ │ demodb.go │ │ │ main.go │ │ │ │ │ └─mysql │ │ demodb.go │ │ main.go │ │ │ ├─gateway │ │ │ gateway.go │ │ │ handlers.go │ │ │ │ │ └─grpc │ │ grpc.go │ │ │ ├─model # data models │ │ base.go │ │ demodb.go │ │ │ └─service # service logics │ demo.go │ demodb.go │ main.go │ ├─pkg # public packages │ ├─build │ │ build.go │ │ │ ├─db │ │ mysql.go │ │ │ ├─health │ │ checks.go │ │ doc.go │ │ handler.go │ │ handler_test.go │ │ timeout.go │ │ timeout_test.go │ │ types.go │ │ │ ├─log │ │ log.go │ │ │ └─utils │ jwt.go │ md5.go │ ├─schema # database scripts │ 000.database.sql │ 001.demodb.sql │ └─third_party # third party resource folder └─google ├─api │ annotations.proto │ http.proto │ ├─protobuf │ any.proto │ descriptor.proto │ duration.proto │ empty.proto │ timestamp.proto │ └─rpc code.proto error_details.proto status.proto 

Upgrade Dependence

To upgrade all dependencies at once for a given module, just run the following from the root directory of your module

This upgrades to the latest or minor patch release

go get -u ./... 

To also upgrade test dependencies

go get -t -u ./... 

Install Dependence

make api_dep_install 

Generate API

make api_gen 

Add Service without Database Operate

  • Proto file definition

    service Demo { rpc Demo(golang-project-layout.api.general.v1.DemoRequest) returns (golang-project-layout.api.general.v1.DemoResponse) { option (google.api.http) = { get: "/api/v1/demo" }; } }
    message Demo { string demo = 1; } message DemoRequest { string demo = 1; } message DemoResponse { Demo demo = 1; }
  • Add RegisterDemoHandler generated by proto in internal/cmd/gateway.go file

    func NewGateway(ctx context.Context, conn *grpc.ClientConn, opts []runtime.ServeMuxOption) (http.Handler, error) { mux := runtime.NewServeMux(opts...) for _, f := range []func(context.Context, *runtime.ServeMux, *grpc.ClientConn) error{ // Add follow line, it is generated by protoc v1.RegisterDemoHandler, } { if err := f(ctx, mux, conn); err != nil { return nil, err } } return mux, nil }
  • Create demo.go file in internal/service folder and implement the proto service apis

    type DemoService struct { // This is generated by protoc projectv1.UnimplementedDemoServer // Add follow line if you need database operate // dao dao.DemoDao } func NewDemoService() *DemoService { return &DemoService{} } // Replace to follow func if you need database operate // func NewDemoService(dao dao.Interface) *DemoService { // return &DemoService{dao: dao.DemoDao()} // } ... // Service implementation code ...
  • Add new demo func in internal/service/main.go file

    func (s *Service) DemoService() DemoService { return *NewDemoService() // Replace to follow line if you need database operate // return *NewDemoService(s.dao) }
  • Add RegisterDemoServer generated by proto in internal/cmd/grpc/grpc.go file

    // ... var daoInterface dao.Interface if daoInterface, err = initDao(); err != nil { return err } // ... // Other services register code // ... demoService := service.NewDemoService() // This is generated by protoc v1.RegisterDemoServer(s, demoService) // ... // Other code // ... go func() { defer s.GracefulStop() <-ctx.Done() }() // ...
  • Implemente Demo API in internal/service/demo.go file

    func (s *DemoService) Demo(ctx context.Context, req *generalv1.DemoRequest) (*generalv1.DemoResponse, error) { return &generalv1.DemoResponse{ Demo: &generalv1.Demo{ Demo: req.Demo, }, }, nil }

Add Service with Database Operate

  • Proto file definition

    service DemoDb { rpc DemoDb(project.api.general.v1.DemoDbRequest) returns (project.api.general.v1.DemoDbResponse) { option (google.api.http) = { get: "/api/v1/demodb" }; } }
    message DemoDb { string demo_db = 1; } message DemoDbRequest { string demo_db = 1; } message DemoDbResponse { DemoDb demo_db = 1; }
  • Add demodb.go in internal/model folder with model definition

    type DemoDb struct { Model DemoDb string `json:"demo_db"` }
  • Add demodb.go dao in internal/dao folder and define interface

    type DemoDbDao interface { DemoDb(ctx context.Context, demoDb string) (*model.DemoDb, error) }
  • Implement dao interface in demodb.go in internal/dao/mysql with New func

    type DemoDbDao struct { Db *gorm.DB } // Implementation interface code func (d DemoDbDao) DemoDb(ctx context.Context, demoDb string) (*model.DemoDb, error) { // This is a demo code ignore it if you have real database logic dDb := &model.DemoDb{ DemoDb: demoDb, } // Add GORM logic here // if err := d.Db.Where("demo_db LIKE ?", "%"+demoDb+"%").Find(&dDb).Error; err != nil { // return nil, err // } return dDb, nil } func NewDemoDbDao(d *gorm.DB) *DemoDbDao { return &DemoDbDao{d} }
  • Add gorm implement func in internal/dao/mysql/main.go file

    func (d *Dao) DemoDbDao() dao.DemoDbDao { // There could be added success after interface implement return NewDemoDbDao(d.Db) }
  • Add the new dao in interface of internal/dao/main.go file

    type Interface interface { // ... // Other dao interfaces // ... DemoDbDao() DemoDbDao }
  • Add RegisterDemoDbHandler generated by proto in internal/cmd/gateway.go file

    func NewGateway(ctx context.Context, conn *grpc.ClientConn, opts []runtime.ServeMuxOption) (http.Handler, error) { mux := runtime.NewServeMux(opts...) for _, f := range []func(context.Context, *runtime.ServeMux, *grpc.ClientConn) error{ // ... // Other code // ... // Add follow line, it is generated by protoc v1.RegisterDemoDbHandler, } { if err := f(ctx, mux, conn); err != nil { return nil, err } } return mux, nil }
  • Create demodb.go file in internal/service folder and implement the proto service apis

    type DemoDbService struct { // This is generated by protoc projectv1.UnimplementedDemoDbServer // Remove follow line if you don't need database operate dao dao.DemoDbDao } func NewDemoDbService(dao dao.Interface) *DemoDbService { return &DemoDbService{dao: dao.DemoDbDao()} } // Replace to follow func if you don't need database operate // func NewDemoDbService() *DemoDbService { // return &DemoDbService{} // } ... // Service implementation code ...
  • Add new demo func in internal/service/main.go file

    func (s *Service) DemoDbService() DemoDbService { return *NewDemoDbService(s.dao) // Replace to follow line if you don't need database operate // return *NewDemoService() }
  • Add RegisterDemoDbServer generated by proto in internal/cmd/grpc/grpc.go file

    // ... var daoInterface dao.Interface if daoInterface, err = initDao(); err != nil { return err } // ... // Other services register code // ... demoDbService := service.NewDemoDbService(daoInterface) // This is generated by protoc v1.RegisterDemoDbServer(s, demoDbService) // ... // Other code // ... go func() { defer s.GracefulStop() <-ctx.Done() }() // ...
  • Implemente Demo API in internal/service/demodb.go file

    func (s *DemoDbService) DemoDb(ctx context.Context, req *generalv1.DemoDbRequest) (*generalv1.DemoDbResponse, error) { demoDb, err := s.dao.DemoDb(ctx, req.DemoDb) if err != nil { result := status.Convert(err) if result.Code() == codes.NotFound { return nil, status.Errorf(codes.NotFound, "get err: %s not found", req.DemoDb) } return nil, status.Error(codes.Unknown, err.Error()) } return &generalv1.DemoDbResponse{ DemoDb: &generalv1.DemoDb{ DemoDb: demoDb.DemoDb, }, }, nil }

About

Standard HTTP and GRPC Go Project Layout with Protobuf and GORM

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published