├── 1 ├── main.go └── server │ └── server.go ├── 2 ├── main.go └── server │ └── server.go ├── 3 ├── loggerfx │ └── loggerfx.go ├── server │ └── server.go └── main.go ├── 4 ├── rpc │ ├── user.go │ └── rpc.go ├── loggerfx │ └── loggerfx.go ├── http │ └── http.go └── main.go ├── 5 ├── loggerfx │ └── loggerfx.go ├── proto │ └── user.proto ├── http │ └── http.go ├── rpc │ └── rpc.go └── main.go ├── .gitignore └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | *.pb.go -------------------------------------------------------------------------------- /4/rpc/user.go: -------------------------------------------------------------------------------- 1 | package rpc 2 | 3 | // User message 4 | type User struct { 5 | // The user name 6 | Name string 7 | // The user age 8 | Age int32 9 | } 10 | -------------------------------------------------------------------------------- /1/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "net/http" 5 | 6 | "medium/medium_webserver_series/1/server" 7 | ) 8 | 9 | func main() { 10 | mux := http.NewServeMux() 11 | server.New(mux) 12 | 13 | http.ListenAndServe(":8080", mux) 14 | } 15 | -------------------------------------------------------------------------------- /3/loggerfx/loggerfx.go: -------------------------------------------------------------------------------- 1 | package loggerfx 2 | 3 | import ( 4 | "go.uber.org/fx" 5 | "go.uber.org/zap" 6 | ) 7 | 8 | // ProvideLogger to fx 9 | func ProvideLogger() *zap.SugaredLogger { 10 | logger, _ := zap.NewProduction() 11 | slogger := logger.Sugar() 12 | 13 | return slogger 14 | } 15 | 16 | // Module provided to fx 17 | var Module = fx.Options( 18 | fx.Provide(ProvideLogger), 19 | ) 20 | -------------------------------------------------------------------------------- /4/loggerfx/loggerfx.go: -------------------------------------------------------------------------------- 1 | package loggerfx 2 | 3 | import ( 4 | "go.uber.org/fx" 5 | "go.uber.org/zap" 6 | ) 7 | 8 | // ProvideLogger to fx 9 | func ProvideLogger() *zap.SugaredLogger { 10 | logger, _ := zap.NewProduction() 11 | slogger := logger.Sugar() 12 | 13 | return slogger 14 | } 15 | 16 | // Module provided to fx 17 | var Module = fx.Options( 18 | fx.Provide(ProvideLogger), 19 | ) 20 | -------------------------------------------------------------------------------- /5/loggerfx/loggerfx.go: -------------------------------------------------------------------------------- 1 | package loggerfx 2 | 3 | import ( 4 | "go.uber.org/fx" 5 | "go.uber.org/zap" 6 | ) 7 | 8 | // ProvideLogger to fx 9 | func ProvideLogger() *zap.SugaredLogger { 10 | logger, _ := zap.NewProduction() 11 | slogger := logger.Sugar() 12 | 13 | return slogger 14 | } 15 | 16 | // Module provided to fx 17 | var Module = fx.Options( 18 | fx.Provide(ProvideLogger), 19 | ) 20 | -------------------------------------------------------------------------------- /5/proto/user.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package users; 3 | 4 | option go_package = ".;user"; 5 | 6 | // Users Service 7 | service Users { 8 | // GetUsers 9 | rpc GetUsers(EmptyReq) returns (GetUsersResponse) {}; 10 | } 11 | 12 | // EmptyReq message 13 | message EmptyReq {} 14 | 15 | // GetUsersResponse message 16 | message GetUsersResponse { 17 | // User message 18 | repeated User users = 1; 19 | } 20 | 21 | // User message 22 | message User { 23 | // The user name 24 | string name = 1; 25 | // The user age 26 | int32 age = 2; 27 | } -------------------------------------------------------------------------------- /4/rpc/rpc.go: -------------------------------------------------------------------------------- 1 | package rpc 2 | 3 | import ( 4 | "net/rpc" 5 | ) 6 | 7 | // Handler is the interface which exposes the User Server methods 8 | type Handler struct { 9 | } 10 | 11 | // New returns the object for the RPC handler 12 | func New() *Handler { 13 | h := &Handler{} 14 | err := rpc.Register(h) 15 | if err != nil { 16 | panic(err) 17 | } 18 | return h 19 | } 20 | 21 | // GetUsers function returns the list of users 22 | func (rh *Handler) GetUsers(payload int, reply *string) error { 23 | // add logic to return users 24 | return nil 25 | } 26 | -------------------------------------------------------------------------------- /2/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "net/http" 6 | 7 | "medium/medium_webserver_series/2/server" 8 | 9 | "go.uber.org/fx" 10 | ) 11 | 12 | func main() { 13 | fx.New( 14 | fx.Provide(http.NewServeMux), 15 | fx.Invoke(server.New), 16 | fx.Invoke(registerHooks), 17 | ).Run() 18 | } 19 | 20 | func registerHooks( 21 | lifecycle fx.Lifecycle, mux *http.ServeMux, 22 | ) { 23 | lifecycle.Append( 24 | fx.Hook{ 25 | OnStart: func(ctx context.Context) error { 26 | go http.ListenAndServe(":8080", mux) 27 | return nil 28 | }, 29 | }, 30 | ) 31 | } -------------------------------------------------------------------------------- /1/server/server.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import "net/http" 4 | 5 | // Handler for http requests 6 | type Handler struct { 7 | mux *http.ServeMux 8 | } 9 | 10 | // New http handler 11 | func New(s *http.ServeMux) *Handler { 12 | h := Handler{s} 13 | h.registerRoutes() 14 | 15 | return &h 16 | } 17 | 18 | // RegisterRoutes for all http endpoints 19 | func (h *Handler) registerRoutes() { 20 | h.mux.HandleFunc("/", h.HelloWorld) 21 | } 22 | 23 | func (h *Handler) HelloWorld(w http.ResponseWriter, r *http.Request) { 24 | w.WriteHeader(200) 25 | w.Write([]byte("Hello World")) 26 | } 27 | -------------------------------------------------------------------------------- /2/server/server.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import "net/http" 4 | 5 | // Handler for http requests 6 | type Handler struct { 7 | mux *http.ServeMux 8 | } 9 | 10 | // New http handler 11 | func New(s *http.ServeMux) *Handler { 12 | h := Handler{s} 13 | h.registerRoutes() 14 | 15 | return &h 16 | } 17 | 18 | // RegisterRoutes for all http endpoints 19 | func (h *Handler) registerRoutes() { 20 | h.mux.HandleFunc("/", h.HelloWorld) 21 | } 22 | 23 | func (h *Handler) HelloWorld(w http.ResponseWriter, r *http.Request) { 24 | w.WriteHeader(200) 25 | w.Write([]byte("Hello World")) 26 | } 27 | -------------------------------------------------------------------------------- /3/server/server.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import "net/http" 4 | 5 | // Handler for http requests 6 | type Handler struct { 7 | mux *http.ServeMux 8 | } 9 | 10 | // New http handler 11 | func New(s *http.ServeMux) *Handler { 12 | h := Handler{s} 13 | h.registerRoutes() 14 | 15 | return &h 16 | } 17 | 18 | // RegisterRoutes for all http endpoints 19 | func (h *Handler) registerRoutes() { 20 | h.mux.HandleFunc("/", h.HelloWorld) 21 | } 22 | 23 | func (h *Handler) HelloWorld(w http.ResponseWriter, r *http.Request) { 24 | w.WriteHeader(200) 25 | w.Write([]byte("Hello World")) 26 | } 27 | -------------------------------------------------------------------------------- /4/http/http.go: -------------------------------------------------------------------------------- 1 | package http 2 | 3 | import ( 4 | "net/http" 5 | ) 6 | 7 | // Handler for http requests 8 | type Handler struct { 9 | mux *http.ServeMux 10 | } 11 | 12 | // New http handler 13 | func New(s *http.ServeMux) *Handler { 14 | h := Handler{s} 15 | h.registerRoutes() 16 | 17 | return &h 18 | } 19 | 20 | // RegisterRoutes for all http endpoints 21 | func (h *Handler) registerRoutes() { 22 | h.mux.HandleFunc("/httpTest", h.HelloWorld) 23 | } 24 | 25 | func (h *Handler) HelloWorld(w http.ResponseWriter, r *http.Request) { 26 | w.WriteHeader(200) 27 | w.Write([]byte("Hello World")) 28 | } 29 | -------------------------------------------------------------------------------- /5/http/http.go: -------------------------------------------------------------------------------- 1 | package http 2 | 3 | import ( 4 | "net/http" 5 | ) 6 | 7 | // Handler for http requests 8 | type Handler struct { 9 | mux *http.ServeMux 10 | } 11 | 12 | // New http handler 13 | func New(s *http.ServeMux) *Handler { 14 | h := Handler{s} 15 | h.registerRoutes() 16 | 17 | return &h 18 | } 19 | 20 | // RegisterRoutes for all http endpoints 21 | func (h *Handler) registerRoutes() { 22 | h.mux.HandleFunc("/httpTest", h.HelloWorld) 23 | } 24 | 25 | func (h *Handler) HelloWorld(w http.ResponseWriter, r *http.Request) { 26 | w.WriteHeader(200) 27 | w.Write([]byte("Hello World")) 28 | } 29 | -------------------------------------------------------------------------------- /5/rpc/rpc.go: -------------------------------------------------------------------------------- 1 | package rpc 2 | 3 | import ( 4 | "context" 5 | 6 | pb "medium/medium_webserver_series/5/proto" 7 | ) 8 | 9 | type Handler = pb.UsersServer 10 | 11 | // handler is the interface which exposes the User Server methods 12 | type handler struct { 13 | pb.UnimplementedUsersServer 14 | } 15 | 16 | // New returns the object for the RPC handler 17 | func New() (Handler, error) { 18 | return &handler{}, nil 19 | } 20 | 21 | // GetUsers function returns the list of users 22 | func (h *handler) GetUsers(ctx context.Context, r *pb.EmptyReq) (*pb.GetUsersResponse, error) { 23 | return &pb.GetUsersResponse{ 24 | Users: []*pb.User{ 25 | { 26 | Name: "test user", 27 | Age: 10, 28 | }, 29 | }, 30 | }, nil 31 | } 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Medium Web Server Series 2 | 3 | This repository contains code retaled to articles published on Medium. 4 | 5 | List of articles: 6 | 7 | Folder | Article 8 | --- | --- 9 | 1 | [GoLang: Building a Web Server](https://levelup.gitconnected.com/golang-building-a-web-server-2d34d4f90fa1) 10 | 2 | [Dependency Injection in Go using Fx](https://medium.com/swlh/dependency-injection-in-go-using-fx-6a623c5c5e01) 11 | 3 | [Writing Fx Modules](https://levelup.gitconnected.com/writing-fx-modules-517193b9c4f0) 12 | 4 | [Writing an RPC server in Go](https://levelup.gitconnected.com/writing-an-rpc-server-in-go-eb9afd56d1e1) 13 | 5 | [Using gRPC and ProtoBuf in GoLang](https://sumit-agarwal.medium.com/using-grpc-and-protobuf-in-golang-9c218d662db3) 14 | 15 | -------------------------------------------------------------------------------- /3/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "net/http" 6 | 7 | "medium/medium_webserver_series/3/loggerfx" 8 | "medium/medium_webserver_series/3/server" 9 | 10 | "go.uber.org/fx" 11 | "go.uber.org/zap" 12 | ) 13 | 14 | func main() { 15 | fx.New( 16 | fx.Provide(http.NewServeMux), 17 | fx.Invoke(server.New), 18 | fx.Invoke(registerHooks), 19 | loggerfx.Module, 20 | ).Run() 21 | } 22 | 23 | func registerHooks( 24 | lifecycle fx.Lifecycle, mux *http.ServeMux, logger *zap.SugaredLogger, 25 | ) { 26 | lifecycle.Append( 27 | fx.Hook{ 28 | OnStart: func(context.Context) error { 29 | logger.Info("Listening on localhost:8080") 30 | go http.ListenAndServe(":8080", mux) 31 | return nil 32 | }, 33 | OnStop: func(context.Context) error { 34 | return logger.Sync() 35 | }, 36 | }, 37 | ) 38 | } 39 | -------------------------------------------------------------------------------- /4/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "net" 6 | "net/http" 7 | "net/rpc" 8 | 9 | httpServer "medium/medium_webserver_series/4/http" 10 | "medium/medium_webserver_series/4/loggerfx" 11 | rpcServer "medium/medium_webserver_series/4/rpc" 12 | 13 | "go.uber.org/fx" 14 | "go.uber.org/zap" 15 | ) 16 | 17 | func main() { 18 | fx.New( 19 | fx.Provide(http.NewServeMux), 20 | fx.Provide(rpcServer.New), 21 | fx.Invoke(httpServer.New), 22 | fx.Invoke(registerHooks), 23 | loggerfx.Module, 24 | ).Run() 25 | } 26 | 27 | func registerHooks( 28 | lifecycle fx.Lifecycle, mux *http.ServeMux, logger *zap.SugaredLogger, 29 | ) { 30 | lifecycle.Append( 31 | fx.Hook{ 32 | OnStart: func(context.Context) error { 33 | 34 | // start the rpc server 35 | l, err := net.Listen("tcp", ":8081") 36 | logger.Errorf("Error while starting rpc server: %+v", err) 37 | go func() { 38 | for { 39 | rpc.Accept(l) 40 | } 41 | }() 42 | logger.Info("Listening on port 8081 for RPC requests") 43 | 44 | 45 | // start the http server 46 | logger.Info("Listening on localhost:8080 for HTTP requests") 47 | go http.ListenAndServe(":8080", mux) // we will look into how to gracefully handle these errors later 48 | 49 | return nil 50 | }, 51 | OnStop: func(context.Context) error { 52 | return logger.Sync() 53 | }, 54 | }, 55 | ) 56 | } 57 | -------------------------------------------------------------------------------- /5/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "net" 6 | "net/http" 7 | 8 | httpServer "medium/medium_webserver_series/5/http" 9 | "medium/medium_webserver_series/5/loggerfx" 10 | pb "medium/medium_webserver_series/5/proto" 11 | rpcServer "medium/medium_webserver_series/5/rpc" 12 | 13 | "go.uber.org/fx" 14 | "go.uber.org/zap" 15 | "google.golang.org/grpc" 16 | ) 17 | 18 | func main() { 19 | fx.New( 20 | fx.Provide(http.NewServeMux), 21 | fx.Provide(rpcServer.New), 22 | fx.Invoke(httpServer.New), 23 | fx.Invoke(registerHooks), 24 | loggerfx.Module, 25 | ).Run() 26 | } 27 | 28 | func registerHooks( 29 | lifecycle fx.Lifecycle, mux *http.ServeMux, logger *zap.SugaredLogger, rpcServer rpcServer.Handler, 30 | ) { 31 | lifecycle.Append( 32 | fx.Hook{ 33 | OnStart: func(context.Context) error { 34 | 35 | // rpc server 36 | lis, err := net.Listen("tcp", ":8081") 37 | if err != nil { 38 | logger.Fatalf("failed to listen: %v", err) 39 | } 40 | var opts []grpc.ServerOption 41 | grpcServer := grpc.NewServer(opts...) 42 | pb.RegisterUsersServer(grpcServer, rpcServer) 43 | go grpcServer.Serve(lis) 44 | 45 | // start the http server 46 | logger.Info("Listening on localhost:8080 for HTTP requests") 47 | go http.ListenAndServe(":8080", mux) // we will look into how to gracefully handle these errors later 48 | 49 | return nil 50 | }, 51 | OnStop: func(context.Context) error { 52 | return logger.Sync() 53 | }, 54 | }, 55 | ) 56 | } 57 | --------------------------------------------------------------------------------