├── README.md ├── account ├── service.go ├── user.go ├── server.go ├── endpoint.go ├── repo.go ├── reqresp.go └── logic.go ├── go.mod ├── go.sum └── main.go /README.md: -------------------------------------------------------------------------------- 1 | # go-kit-tutorial -------------------------------------------------------------------------------- /account/service.go: -------------------------------------------------------------------------------- 1 | package account 2 | 3 | import "context" 4 | 5 | type Service interface { 6 | CreateUser(ctx context.Context, email string, password string) (string, error) 7 | GetUser(ctx context.Context, id string) (string, error) 8 | } 9 | 10 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module gokit-example 2 | 3 | go 1.13 4 | 5 | require ( 6 | github.com/go-kit/kit v0.9.0 7 | github.com/go-logfmt/logfmt v0.4.0 // indirect 8 | github.com/go-stack/stack v1.8.0 // indirect 9 | github.com/gofrs/uuid v3.2.0+incompatible 10 | github.com/gorilla/mux v1.7.3 11 | github.com/lib/pq v1.2.0 12 | ) 13 | -------------------------------------------------------------------------------- /account/user.go: -------------------------------------------------------------------------------- 1 | package account 2 | 3 | import "context" 4 | 5 | type User struct { 6 | ID string `json:"id,omitempty"` 7 | Email string `json:"email"` 8 | Password string `json:"password"` 9 | } 10 | 11 | type Repository interface { 12 | CreateUser(ctx context.Context, user User) error 13 | GetUser(ctx context.Context, id string) (string, error) 14 | } -------------------------------------------------------------------------------- /account/server.go: -------------------------------------------------------------------------------- 1 | package account 2 | 3 | import ( 4 | "context" 5 | "net/http" 6 | 7 | "github.com/gorilla/mux" 8 | httptransport "github.com/go-kit/kit/transport/http" 9 | ) 10 | 11 | func NewHTTPServer(ctx context.Context, endpoints Endpoints) http.Handler { 12 | r := mux.NewRouter() 13 | r.Use(commonMiddleware) 14 | 15 | r.Methods("POST").Path("/user").Handler(httptransport.NewServer( 16 | endpoints.CreateUser, 17 | decodeUserReq, 18 | encodeResponse, 19 | )) 20 | 21 | r.Methods("GET").Path("/user/{id}").Handler(httptransport.NewServer( 22 | endpoints.GetUser, 23 | decodeEmailReq, 24 | encodeResponse, 25 | )) 26 | 27 | return r 28 | 29 | } 30 | 31 | func commonMiddleware(next http.Handler) http.Handler { 32 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 33 | w.Header().Add("Content-Type", "application/json") 34 | next.ServeHTTP(w, r) 35 | }) 36 | } 37 | -------------------------------------------------------------------------------- /account/endpoint.go: -------------------------------------------------------------------------------- 1 | package account 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/go-kit/kit/endpoint" 7 | ) 8 | 9 | type Endpoints struct { 10 | CreateUser endpoint.Endpoint 11 | GetUser endpoint.Endpoint 12 | } 13 | 14 | func MakeEndpoints(s Service) Endpoints { 15 | return Endpoints{ 16 | CreateUser: makeCreateUserEndpoint(s), 17 | GetUser: makeGetUserEndpoint(s), 18 | } 19 | } 20 | 21 | func makeCreateUserEndpoint(s Service) endpoint.Endpoint { 22 | return func(ctx context.Context, request interface{}) (interface{}, error) { 23 | req := request.(CreateUserRequest) 24 | ok, err := s.CreateUser(ctx, req.Email, req.Password) 25 | return CreateUserResponse{Ok: ok}, err 26 | } 27 | } 28 | 29 | func makeGetUserEndpoint(s Service) endpoint.Endpoint { 30 | return func(ctx context.Context, request interface{}) (interface{}, error) { 31 | req := request.(GetUserRequest) 32 | email, err := s.GetUser(ctx, req.Id) 33 | 34 | return GetUserResponse{ 35 | Email: email, 36 | }, err 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/go-kit/kit v0.9.0 h1:wDJmvq38kDhkVxi50ni9ykkdUr1PKgqKOoi01fa0Mdk= 2 | github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 3 | github.com/go-logfmt/logfmt v0.4.0 h1:MP4Eh7ZCb31lleYCFuwm0oe4/YGak+5l1vA2NOE80nA= 4 | github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= 5 | github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= 6 | github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= 7 | github.com/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE= 8 | github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= 9 | github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw= 10 | github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= 11 | github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= 12 | github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0= 13 | github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= 14 | -------------------------------------------------------------------------------- /account/repo.go: -------------------------------------------------------------------------------- 1 | package account 2 | 3 | import ( 4 | "context" 5 | "database/sql" 6 | "errors" 7 | 8 | "github.com/go-kit/kit/log" 9 | ) 10 | 11 | var RepoErr = errors.New("Unable to handle Repo Request") 12 | 13 | type repo struct { 14 | db *sql.DB 15 | logger log.Logger 16 | } 17 | 18 | func NewRepo(db *sql.DB, logger log.Logger) Repository { 19 | return &repo{ 20 | db: db, 21 | logger: log.With(logger, "repo", "sql"), 22 | } 23 | } 24 | 25 | func (repo *repo) CreateUser(ctx context.Context, user User) error { 26 | sql := ` 27 | INSERT INTO users (id, email, password) 28 | VALUES ($1, $2, $3)` 29 | 30 | if user.Email == "" || user.Password == "" { 31 | return RepoErr 32 | } 33 | 34 | _, err := repo.db.ExecContext(ctx, sql, user.ID, user.Email, user.Password) 35 | if err != nil { 36 | return err 37 | } 38 | return nil 39 | } 40 | 41 | func (repo *repo) GetUser(ctx context.Context, id string) (string, error) { 42 | var email string 43 | err := repo.db.QueryRow("SELECT email FROM users WHERE id=$1", id).Scan(&email) 44 | if err != nil { 45 | return "", RepoErr 46 | } 47 | 48 | return email, nil 49 | } 50 | -------------------------------------------------------------------------------- /account/reqresp.go: -------------------------------------------------------------------------------- 1 | package account 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "net/http" 7 | 8 | "github.com/gorilla/mux" 9 | ) 10 | 11 | type ( 12 | CreateUserRequest struct { 13 | Email string `json:"email"` 14 | Password string `json:"password"` 15 | } 16 | CreateUserResponse struct { 17 | Ok string `json:"ok"` 18 | } 19 | 20 | GetUserRequest struct { 21 | Id string `json:"id"` 22 | } 23 | GetUserResponse struct { 24 | Email string `json:"email"` 25 | } 26 | ) 27 | 28 | 29 | func encodeResponse(ctx context.Context, w http.ResponseWriter, response interface{}) error { 30 | return json.NewEncoder(w).Encode(response) 31 | } 32 | 33 | func decodeUserReq(ctx context.Context, r *http.Request) (interface{}, error) { 34 | var req CreateUserRequest 35 | err := json.NewDecoder(r.Body).Decode(&req) 36 | if err != nil { 37 | return nil, err 38 | } 39 | return req, nil 40 | } 41 | 42 | func decodeEmailReq(ctx context.Context, r *http.Request) (interface{}, error) { 43 | var req GetUserRequest 44 | vars := mux.Vars(r) 45 | 46 | req = GetUserRequest{ 47 | Id: vars["id"], 48 | } 49 | return req, nil 50 | } 51 | 52 | -------------------------------------------------------------------------------- /account/logic.go: -------------------------------------------------------------------------------- 1 | package account 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/go-kit/kit/log" 7 | "github.com/go-kit/kit/log/level" 8 | "github.com/gofrs/uuid" 9 | ) 10 | 11 | type service struct { 12 | repostory Repository 13 | logger log.Logger 14 | } 15 | 16 | func NewService(rep Repository, logger log.Logger) Service { 17 | return &service{ 18 | repostory: rep, 19 | logger: logger, 20 | } 21 | } 22 | 23 | func (s service) CreateUser(ctx context.Context, email string, password string) (string, error) { 24 | logger := log.With(s.logger, "method", "CreateUser") 25 | 26 | uuid, _ := uuid.NewV4() 27 | id := uuid.String() 28 | user := User{ 29 | ID: id, 30 | Email: email, 31 | Password: password, 32 | } 33 | 34 | if err := s.repostory.CreateUser(ctx, user); err != nil { 35 | level.Error(logger).Log("err", err) 36 | return "", err 37 | } 38 | 39 | logger.Log("create user", id) 40 | 41 | return "Success", nil 42 | } 43 | 44 | func (s service) GetUser(ctx context.Context, id string) (string, error) { 45 | logger := log.With(s.logger, "method", "GetUser") 46 | 47 | email, err := s.repostory.GetUser(ctx, id) 48 | 49 | if err != nil { 50 | level.Error(logger).Log("err", err) 51 | return "", err 52 | } 53 | 54 | logger.Log("Get user", id) 55 | 56 | return email, nil 57 | } 58 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "database/sql" 6 | "flag" 7 | "fmt" 8 | 9 | _ "github.com/lib/pq" 10 | 11 | "github.com/go-kit/kit/log" 12 | 13 | "github.com/go-kit/kit/log/level" 14 | 15 | "net/http" 16 | "os" 17 | "os/signal" 18 | "syscall" 19 | 20 | "gokit-example/account" 21 | ) 22 | 23 | const dbsource = "postgresql://postgres:postgres@localhost:5432/gokitexample?sslmode=disable" 24 | 25 | func main() { 26 | var httpAddr = flag.String("http", ":8080", "http listen address") 27 | var logger log.Logger 28 | { 29 | logger = log.NewLogfmtLogger(os.Stderr) 30 | logger = log.NewSyncLogger(logger) 31 | logger = log.With(logger, 32 | "service", "account", 33 | "time:", log.DefaultTimestampUTC, 34 | "caller", log.DefaultCaller, 35 | ) 36 | } 37 | 38 | level.Info(logger).Log("msg", "service started") 39 | defer level.Info(logger).Log("msg", "service ended") 40 | 41 | var db *sql.DB 42 | { 43 | var err error 44 | 45 | db, err = sql.Open("postgres", dbsource) 46 | if err != nil { 47 | level.Error(logger).Log("exit", err) 48 | os.Exit(-1) 49 | } 50 | 51 | } 52 | 53 | flag.Parse() 54 | ctx := context.Background() 55 | var srv account.Service 56 | { 57 | repository := account.NewRepo(db, logger) 58 | 59 | srv = account.NewService(repository, logger) 60 | } 61 | 62 | errs := make(chan error) 63 | 64 | go func() { 65 | c := make(chan os.Signal, 1) 66 | signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) 67 | errs <- fmt.Errorf("%s", <-c) 68 | }() 69 | 70 | endpoints := account.MakeEndpoints(srv) 71 | 72 | go func() { 73 | fmt.Println("listening on port", *httpAddr) 74 | handler := account.NewHTTPServer(ctx, endpoints) 75 | errs <- http.ListenAndServe(*httpAddr, handler) 76 | }() 77 | 78 | level.Error(logger).Log("exit", <-errs) 79 | } 80 | --------------------------------------------------------------------------------