istiofy

module
v0.0.0-...-30e41e6 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 16, 2024 License: Apache-2.0

README

istiofy

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/project/swagger, you can test the APIs.

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
│  │
│  └─istiofy
│      └─v1
│              istiofy.proto
│              README.md
│
├─cmd # project start from here
│  └─istiofy
│          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 Datbase Operate

  • Proto file definition

    service Demo {
        rpc Demo(istiofy.api.general.v1.DemoRequest) returns (istiofy.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
    }
    

Directories

Path Synopsis
cmd
docs
internal
cmd
dao
pkg
build
package build conatins the stuffs about build information such as: build time, git version and so on
package build conatins the stuffs about build information such as: build time, git version and so on
db
health
Package health helps you implement Kubernetes liveness and readiness checks for your application.
Package health helps you implement Kubernetes liveness and readiness checks for your application.
log

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL