├── .gitignore ├── Makefile ├── README ├── main.go ├── pb ├── hello.pb.go ├── hello.pb.gw.go ├── hello.pb.validate.go ├── hello.proto └── hello.swagger.json └── server └── server.go /.gitignore: -------------------------------------------------------------------------------- 1 | grpc-rest-api-example 2 | helloworld 3 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | TARGET=helloworld 2 | 3 | all: clean build 4 | 5 | clean: 6 | rm -rf $(TARGET) 7 | 8 | build: 9 | go build -o $(TARGET) main.go 10 | 11 | proto: 12 | protoc -I/usr/local/include -I. \ 13 | -I${GOPATH}/src \ 14 | -I${GOPATH}/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \ 15 | -I${GOPATH}/src/github.com/lyft/protoc-gen-validate \ 16 | --go_out=plugins=grpc:. \ 17 | pb/hello.proto 18 | protoc -I/usr/local/include -I. \ 19 | -I${GOPATH}/src \ 20 | -I${GOPATH}/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \ 21 | -I${GOPATH}/src/github.com/lyft/protoc-gen-validate \ 22 | --grpc-gateway_out=logtostderr=true:. \ 23 | pb/hello.proto 24 | protoc -I/usr/local/include -I. \ 25 | -I${GOPATH}/src \ 26 | -I${GOPATH}/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \ 27 | -I${GOPATH}/src/github.com/lyft/protoc-gen-validate \ 28 | --swagger_out=logtostderr=true:. \ 29 | pb/hello.proto 30 | protoc -I/usr/local/include -I. \ 31 | -I${GOPATH}/src \ 32 | -I${GOPATH}/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \ 33 | -I${GOPATH}/src/github.com/lyft/protoc-gen-validate \ 34 | --validate_out="lang=go:." \ 35 | pb/hello.proto 36 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maddevsio/grpc-rest-api-example/53ae86811118e7d316496d02d95053f3a47626fc/README -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "github.com/maddevsio/grpc-rest-api-example/server" 4 | 5 | func main() { 6 | g := server.New() 7 | g.Start() 8 | g.WaitStop() 9 | } 10 | -------------------------------------------------------------------------------- /pb/hello.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go. DO NOT EDIT. 2 | // source: pb/hello.proto 3 | 4 | package helloworld 5 | 6 | import ( 7 | context "context" 8 | fmt "fmt" 9 | proto "github.com/golang/protobuf/proto" 10 | _ "github.com/lyft/protoc-gen-validate/validate" 11 | _ "google.golang.org/genproto/googleapis/api/annotations" 12 | grpc "google.golang.org/grpc" 13 | math "math" 14 | ) 15 | 16 | // Reference imports to suppress errors if they are not otherwise used. 17 | var _ = proto.Marshal 18 | var _ = fmt.Errorf 19 | var _ = math.Inf 20 | 21 | // This is a compile-time assertion to ensure that this generated file 22 | // is compatible with the proto package it is being compiled against. 23 | // A compilation error at this line likely means your copy of the 24 | // proto package needs to be updated. 25 | const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package 26 | 27 | type HelloRequest struct { 28 | Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` 29 | XXX_NoUnkeyedLiteral struct{} `json:"-"` 30 | XXX_unrecognized []byte `json:"-"` 31 | XXX_sizecache int32 `json:"-"` 32 | } 33 | 34 | func (m *HelloRequest) Reset() { *m = HelloRequest{} } 35 | func (m *HelloRequest) String() string { return proto.CompactTextString(m) } 36 | func (*HelloRequest) ProtoMessage() {} 37 | func (*HelloRequest) Descriptor() ([]byte, []int) { 38 | return fileDescriptor_b9418a89e4940c19, []int{0} 39 | } 40 | 41 | func (m *HelloRequest) XXX_Unmarshal(b []byte) error { 42 | return xxx_messageInfo_HelloRequest.Unmarshal(m, b) 43 | } 44 | func (m *HelloRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { 45 | return xxx_messageInfo_HelloRequest.Marshal(b, m, deterministic) 46 | } 47 | func (m *HelloRequest) XXX_Merge(src proto.Message) { 48 | xxx_messageInfo_HelloRequest.Merge(m, src) 49 | } 50 | func (m *HelloRequest) XXX_Size() int { 51 | return xxx_messageInfo_HelloRequest.Size(m) 52 | } 53 | func (m *HelloRequest) XXX_DiscardUnknown() { 54 | xxx_messageInfo_HelloRequest.DiscardUnknown(m) 55 | } 56 | 57 | var xxx_messageInfo_HelloRequest proto.InternalMessageInfo 58 | 59 | func (m *HelloRequest) GetName() string { 60 | if m != nil { 61 | return m.Name 62 | } 63 | return "" 64 | } 65 | 66 | type HelloReply struct { 67 | Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` 68 | XXX_NoUnkeyedLiteral struct{} `json:"-"` 69 | XXX_unrecognized []byte `json:"-"` 70 | XXX_sizecache int32 `json:"-"` 71 | } 72 | 73 | func (m *HelloReply) Reset() { *m = HelloReply{} } 74 | func (m *HelloReply) String() string { return proto.CompactTextString(m) } 75 | func (*HelloReply) ProtoMessage() {} 76 | func (*HelloReply) Descriptor() ([]byte, []int) { 77 | return fileDescriptor_b9418a89e4940c19, []int{1} 78 | } 79 | 80 | func (m *HelloReply) XXX_Unmarshal(b []byte) error { 81 | return xxx_messageInfo_HelloReply.Unmarshal(m, b) 82 | } 83 | func (m *HelloReply) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { 84 | return xxx_messageInfo_HelloReply.Marshal(b, m, deterministic) 85 | } 86 | func (m *HelloReply) XXX_Merge(src proto.Message) { 87 | xxx_messageInfo_HelloReply.Merge(m, src) 88 | } 89 | func (m *HelloReply) XXX_Size() int { 90 | return xxx_messageInfo_HelloReply.Size(m) 91 | } 92 | func (m *HelloReply) XXX_DiscardUnknown() { 93 | xxx_messageInfo_HelloReply.DiscardUnknown(m) 94 | } 95 | 96 | var xxx_messageInfo_HelloReply proto.InternalMessageInfo 97 | 98 | func (m *HelloReply) GetMessage() string { 99 | if m != nil { 100 | return m.Message 101 | } 102 | return "" 103 | } 104 | 105 | func init() { 106 | proto.RegisterType((*HelloRequest)(nil), "helloworld.HelloRequest") 107 | proto.RegisterType((*HelloReply)(nil), "helloworld.HelloReply") 108 | } 109 | 110 | func init() { proto.RegisterFile("pb/hello.proto", fileDescriptor_b9418a89e4940c19) } 111 | 112 | var fileDescriptor_b9418a89e4940c19 = []byte{ 113 | // 217 bytes of a gzipped FileDescriptorProto 114 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x2b, 0x48, 0xd2, 0xcf, 115 | 0x48, 0xcd, 0xc9, 0xc9, 0xd7, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x02, 0x73, 0xca, 0xf3, 116 | 0x8b, 0x72, 0x52, 0xa4, 0x64, 0xd2, 0xf3, 0xf3, 0xd3, 0x73, 0x52, 0xf5, 0x13, 0x0b, 0x32, 0xf5, 117 | 0x13, 0xf3, 0xf2, 0xf2, 0x4b, 0x12, 0x4b, 0x32, 0xf3, 0xf3, 0x8a, 0x21, 0x2a, 0xa5, 0xc4, 0xcb, 118 | 0x12, 0x73, 0x32, 0x53, 0x12, 0x4b, 0x52, 0xf5, 0x61, 0x0c, 0x88, 0x84, 0x92, 0x1e, 0x17, 0x8f, 119 | 0x07, 0xc8, 0x90, 0xa0, 0xd4, 0xc2, 0xd2, 0xd4, 0xe2, 0x12, 0x21, 0x39, 0x2e, 0x96, 0xbc, 0xc4, 120 | 0xdc, 0x54, 0x09, 0x46, 0x05, 0x46, 0x0d, 0x4e, 0x27, 0xae, 0x5d, 0x2f, 0x0f, 0x30, 0xb3, 0x16, 121 | 0x31, 0xcf, 0x60, 0x64, 0x0e, 0x02, 0x8b, 0x2b, 0xa9, 0x71, 0x71, 0x41, 0xd5, 0x17, 0xe4, 0x54, 122 | 0x0a, 0x49, 0x70, 0xb1, 0xe7, 0xa6, 0x16, 0x17, 0x27, 0xa6, 0x43, 0x35, 0x04, 0xc1, 0xb8, 0x46, 123 | 0x49, 0x5c, 0xec, 0xee, 0x45, 0xa9, 0xa9, 0x25, 0xa9, 0x45, 0x42, 0xe1, 0x5c, 0x1c, 0xc1, 0x89, 124 | 0x95, 0x60, 0x5d, 0x42, 0x12, 0x7a, 0x08, 0x27, 0xeb, 0x21, 0x5b, 0x2c, 0x25, 0x86, 0x45, 0xa6, 125 | 0x20, 0xa7, 0x52, 0x49, 0xb2, 0xe9, 0xf2, 0x93, 0xc9, 0x4c, 0xc2, 0x42, 0x82, 0x60, 0x9f, 0x81, 126 | 0xd5, 0xe8, 0x57, 0x83, 0x9c, 0x52, 0x9b, 0xc4, 0x06, 0xf6, 0x82, 0x31, 0x20, 0x00, 0x00, 0xff, 127 | 0xff, 0xa2, 0x0e, 0x88, 0xdb, 0x17, 0x01, 0x00, 0x00, 128 | } 129 | 130 | // Reference imports to suppress errors if they are not otherwise used. 131 | var _ context.Context 132 | var _ grpc.ClientConn 133 | 134 | // This is a compile-time assertion to ensure that this generated file 135 | // is compatible with the grpc package it is being compiled against. 136 | const _ = grpc.SupportPackageIsVersion4 137 | 138 | // GreeterClient is the client API for Greeter service. 139 | // 140 | // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. 141 | type GreeterClient interface { 142 | SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloReply, error) 143 | } 144 | 145 | type greeterClient struct { 146 | cc *grpc.ClientConn 147 | } 148 | 149 | func NewGreeterClient(cc *grpc.ClientConn) GreeterClient { 150 | return &greeterClient{cc} 151 | } 152 | 153 | func (c *greeterClient) SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloReply, error) { 154 | out := new(HelloReply) 155 | err := c.cc.Invoke(ctx, "/helloworld.Greeter/SayHello", in, out, opts...) 156 | if err != nil { 157 | return nil, err 158 | } 159 | return out, nil 160 | } 161 | 162 | // GreeterServer is the server API for Greeter service. 163 | type GreeterServer interface { 164 | SayHello(context.Context, *HelloRequest) (*HelloReply, error) 165 | } 166 | 167 | func RegisterGreeterServer(s *grpc.Server, srv GreeterServer) { 168 | s.RegisterService(&_Greeter_serviceDesc, srv) 169 | } 170 | 171 | func _Greeter_SayHello_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 172 | in := new(HelloRequest) 173 | if err := dec(in); err != nil { 174 | return nil, err 175 | } 176 | if interceptor == nil { 177 | return srv.(GreeterServer).SayHello(ctx, in) 178 | } 179 | info := &grpc.UnaryServerInfo{ 180 | Server: srv, 181 | FullMethod: "/helloworld.Greeter/SayHello", 182 | } 183 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 184 | return srv.(GreeterServer).SayHello(ctx, req.(*HelloRequest)) 185 | } 186 | return interceptor(ctx, in, info, handler) 187 | } 188 | 189 | var _Greeter_serviceDesc = grpc.ServiceDesc{ 190 | ServiceName: "helloworld.Greeter", 191 | HandlerType: (*GreeterServer)(nil), 192 | Methods: []grpc.MethodDesc{ 193 | { 194 | MethodName: "SayHello", 195 | Handler: _Greeter_SayHello_Handler, 196 | }, 197 | }, 198 | Streams: []grpc.StreamDesc{}, 199 | Metadata: "pb/hello.proto", 200 | } 201 | -------------------------------------------------------------------------------- /pb/hello.pb.gw.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. 2 | // source: pb/hello.proto 3 | 4 | /* 5 | Package helloworld is a reverse proxy. 6 | 7 | It translates gRPC into RESTful JSON APIs. 8 | */ 9 | package helloworld 10 | 11 | import ( 12 | "io" 13 | "net/http" 14 | 15 | "github.com/golang/protobuf/proto" 16 | "github.com/grpc-ecosystem/grpc-gateway/runtime" 17 | "github.com/grpc-ecosystem/grpc-gateway/utilities" 18 | "golang.org/x/net/context" 19 | "google.golang.org/grpc" 20 | "google.golang.org/grpc/codes" 21 | "google.golang.org/grpc/grpclog" 22 | "google.golang.org/grpc/status" 23 | ) 24 | 25 | var _ codes.Code 26 | var _ io.Reader 27 | var _ status.Status 28 | var _ = runtime.String 29 | var _ = utilities.NewDoubleArray 30 | 31 | func request_Greeter_SayHello_0(ctx context.Context, marshaler runtime.Marshaler, client GreeterClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { 32 | var protoReq HelloRequest 33 | var metadata runtime.ServerMetadata 34 | 35 | var ( 36 | val string 37 | ok bool 38 | err error 39 | _ = err 40 | ) 41 | 42 | val, ok = pathParams["name"] 43 | if !ok { 44 | return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "name") 45 | } 46 | 47 | protoReq.Name, err = runtime.String(val) 48 | 49 | if err != nil { 50 | return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "name", err) 51 | } 52 | 53 | msg, err := client.SayHello(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) 54 | return msg, metadata, err 55 | 56 | } 57 | 58 | // RegisterGreeterHandlerFromEndpoint is same as RegisterGreeterHandler but 59 | // automatically dials to "endpoint" and closes the connection when "ctx" gets done. 60 | func RegisterGreeterHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) { 61 | conn, err := grpc.Dial(endpoint, opts...) 62 | if err != nil { 63 | return err 64 | } 65 | defer func() { 66 | if err != nil { 67 | if cerr := conn.Close(); cerr != nil { 68 | grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) 69 | } 70 | return 71 | } 72 | go func() { 73 | <-ctx.Done() 74 | if cerr := conn.Close(); cerr != nil { 75 | grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) 76 | } 77 | }() 78 | }() 79 | 80 | return RegisterGreeterHandler(ctx, mux, conn) 81 | } 82 | 83 | // RegisterGreeterHandler registers the http handlers for service Greeter to "mux". 84 | // The handlers forward requests to the grpc endpoint over "conn". 85 | func RegisterGreeterHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error { 86 | return RegisterGreeterHandlerClient(ctx, mux, NewGreeterClient(conn)) 87 | } 88 | 89 | // RegisterGreeterHandlerClient registers the http handlers for service Greeter 90 | // to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "GreeterClient". 91 | // Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "GreeterClient" 92 | // doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in 93 | // "GreeterClient" to call the correct interceptors. 94 | func RegisterGreeterHandlerClient(ctx context.Context, mux *runtime.ServeMux, client GreeterClient) error { 95 | 96 | mux.Handle("GET", pattern_Greeter_SayHello_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { 97 | ctx, cancel := context.WithCancel(req.Context()) 98 | defer cancel() 99 | inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) 100 | rctx, err := runtime.AnnotateContext(ctx, mux, req) 101 | if err != nil { 102 | runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) 103 | return 104 | } 105 | resp, md, err := request_Greeter_SayHello_0(rctx, inboundMarshaler, client, req, pathParams) 106 | ctx = runtime.NewServerMetadataContext(ctx, md) 107 | if err != nil { 108 | runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) 109 | return 110 | } 111 | 112 | forward_Greeter_SayHello_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) 113 | 114 | }) 115 | 116 | return nil 117 | } 118 | 119 | var ( 120 | pattern_Greeter_SayHello_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2}, []string{"api", "hello", "name"}, "")) 121 | ) 122 | 123 | var ( 124 | forward_Greeter_SayHello_0 = runtime.ForwardResponseMessage 125 | ) 126 | -------------------------------------------------------------------------------- /pb/hello.pb.validate.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-validate. DO NOT EDIT. 2 | // source: pb/hello.proto 3 | 4 | package helloworld 5 | 6 | import ( 7 | "bytes" 8 | "errors" 9 | "fmt" 10 | "net" 11 | "net/mail" 12 | "net/url" 13 | "regexp" 14 | "strings" 15 | "time" 16 | "unicode/utf8" 17 | 18 | "github.com/golang/protobuf/ptypes" 19 | ) 20 | 21 | // ensure the imports are used 22 | var ( 23 | _ = bytes.MinRead 24 | _ = errors.New("") 25 | _ = fmt.Print 26 | _ = utf8.UTFMax 27 | _ = (*regexp.Regexp)(nil) 28 | _ = (*strings.Reader)(nil) 29 | _ = net.IPv4len 30 | _ = time.Duration(0) 31 | _ = (*url.URL)(nil) 32 | _ = (*mail.Address)(nil) 33 | _ = ptypes.DynamicAny{} 34 | ) 35 | 36 | // Validate checks the field values on HelloRequest with the rules defined in 37 | // the proto definition for this message. If any rules are violated, an error 38 | // is returned. 39 | func (m *HelloRequest) Validate() error { 40 | if m == nil { 41 | return nil 42 | } 43 | 44 | if utf8.RuneCountInString(m.GetName()) != 3 { 45 | return HelloRequestValidationError{ 46 | field: "Name", 47 | reason: "value length must be 3 runes", 48 | } 49 | 50 | } 51 | 52 | return nil 53 | } 54 | 55 | // HelloRequestValidationError is the validation error returned by 56 | // HelloRequest.Validate if the designated constraints aren't met. 57 | type HelloRequestValidationError struct { 58 | field string 59 | reason string 60 | cause error 61 | key bool 62 | } 63 | 64 | // Field function returns field value. 65 | func (e HelloRequestValidationError) Field() string { return e.field } 66 | 67 | // Reason function returns reason value. 68 | func (e HelloRequestValidationError) Reason() string { return e.reason } 69 | 70 | // Cause function returns cause value. 71 | func (e HelloRequestValidationError) Cause() error { return e.cause } 72 | 73 | // Key function returns key value. 74 | func (e HelloRequestValidationError) Key() bool { return e.key } 75 | 76 | // ErrorName returns error name. 77 | func (e HelloRequestValidationError) ErrorName() string { return "HelloRequestValidationError" } 78 | 79 | // Error satisfies the builtin error interface 80 | func (e HelloRequestValidationError) Error() string { 81 | cause := "" 82 | if e.cause != nil { 83 | cause = fmt.Sprintf(" | caused by: %v", e.cause) 84 | } 85 | 86 | key := "" 87 | if e.key { 88 | key = "key for " 89 | } 90 | 91 | return fmt.Sprintf( 92 | "invalid %sHelloRequest.%s: %s%s", 93 | key, 94 | e.field, 95 | e.reason, 96 | cause) 97 | } 98 | 99 | var _ error = HelloRequestValidationError{} 100 | 101 | var _ interface { 102 | Field() string 103 | Reason() string 104 | Key() bool 105 | Cause() error 106 | ErrorName() string 107 | } = HelloRequestValidationError{} 108 | 109 | // Validate checks the field values on HelloReply with the rules defined in the 110 | // proto definition for this message. If any rules are violated, an error is returned. 111 | func (m *HelloReply) Validate() error { 112 | if m == nil { 113 | return nil 114 | } 115 | 116 | // no validation rules for Message 117 | 118 | return nil 119 | } 120 | 121 | // HelloReplyValidationError is the validation error returned by 122 | // HelloReply.Validate if the designated constraints aren't met. 123 | type HelloReplyValidationError struct { 124 | field string 125 | reason string 126 | cause error 127 | key bool 128 | } 129 | 130 | // Field function returns field value. 131 | func (e HelloReplyValidationError) Field() string { return e.field } 132 | 133 | // Reason function returns reason value. 134 | func (e HelloReplyValidationError) Reason() string { return e.reason } 135 | 136 | // Cause function returns cause value. 137 | func (e HelloReplyValidationError) Cause() error { return e.cause } 138 | 139 | // Key function returns key value. 140 | func (e HelloReplyValidationError) Key() bool { return e.key } 141 | 142 | // ErrorName returns error name. 143 | func (e HelloReplyValidationError) ErrorName() string { return "HelloReplyValidationError" } 144 | 145 | // Error satisfies the builtin error interface 146 | func (e HelloReplyValidationError) Error() string { 147 | cause := "" 148 | if e.cause != nil { 149 | cause = fmt.Sprintf(" | caused by: %v", e.cause) 150 | } 151 | 152 | key := "" 153 | if e.key { 154 | key = "key for " 155 | } 156 | 157 | return fmt.Sprintf( 158 | "invalid %sHelloReply.%s: %s%s", 159 | key, 160 | e.field, 161 | e.reason, 162 | cause) 163 | } 164 | 165 | var _ error = HelloReplyValidationError{} 166 | 167 | var _ interface { 168 | Field() string 169 | Reason() string 170 | Key() bool 171 | Cause() error 172 | ErrorName() string 173 | } = HelloReplyValidationError{} 174 | -------------------------------------------------------------------------------- /pb/hello.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | import "google/api/annotations.proto"; 4 | import "validate/validate.proto"; 5 | 6 | package helloworld; 7 | 8 | service Greeter { 9 | rpc SayHello (HelloRequest) returns (HelloReply) { 10 | option(google.api.http) = { 11 | get: "/api/hello/{name}", 12 | }; 13 | } 14 | } 15 | 16 | message HelloRequest { 17 | string name = 1 [(validate.rules).string.len = 3]; 18 | } 19 | 20 | message HelloReply { 21 | string message = 1; 22 | } 23 | -------------------------------------------------------------------------------- /pb/hello.swagger.json: -------------------------------------------------------------------------------- 1 | { 2 | "swagger": "2.0", 3 | "info": { 4 | "title": "pb/hello.proto", 5 | "version": "version not set" 6 | }, 7 | "schemes": [ 8 | "http", 9 | "https" 10 | ], 11 | "consumes": [ 12 | "application/json" 13 | ], 14 | "produces": [ 15 | "application/json" 16 | ], 17 | "paths": { 18 | "/api/hello/{name}": { 19 | "get": { 20 | "operationId": "SayHello", 21 | "responses": { 22 | "200": { 23 | "description": "A successful response.", 24 | "schema": { 25 | "$ref": "#/definitions/helloworldHelloReply" 26 | } 27 | } 28 | }, 29 | "parameters": [ 30 | { 31 | "name": "name", 32 | "in": "path", 33 | "required": true, 34 | "type": "string" 35 | } 36 | ], 37 | "tags": [ 38 | "Greeter" 39 | ] 40 | } 41 | } 42 | }, 43 | "definitions": { 44 | "helloworldHelloReply": { 45 | "type": "object", 46 | "properties": { 47 | "message": { 48 | "type": "string" 49 | } 50 | } 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /server/server.go: -------------------------------------------------------------------------------- 1 | package server 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "log" 7 | "net" 8 | "net/http" 9 | "sync" 10 | 11 | "github.com/grpc-ecosystem/grpc-gateway/runtime" 12 | pb "github.com/maddevsio/grpc-rest-api-example/pb" 13 | "google.golang.org/grpc" 14 | ) 15 | 16 | // Greeter ... 17 | type Greeter struct { 18 | wg sync.WaitGroup 19 | } 20 | 21 | // New creates new server greeter 22 | func New() *Greeter { 23 | return &Greeter{} 24 | } 25 | 26 | // Start starts server 27 | func (g *Greeter) Start() { 28 | g.wg.Add(1) 29 | go func() { 30 | log.Fatal(g.startGRPC()) 31 | g.wg.Done() 32 | }() 33 | g.wg.Add(1) 34 | go func() { 35 | log.Fatal(g.startREST()) 36 | g.wg.Done() 37 | }() 38 | } 39 | func (g *Greeter) WaitStop() { 40 | g.wg.Wait() 41 | } 42 | func (g *Greeter) startGRPC() error { 43 | lis, err := net.Listen("tcp", "localhost:8080") 44 | if err != nil { 45 | return err 46 | } 47 | grpcServer := grpc.NewServer() 48 | pb.RegisterGreeterServer(grpcServer, g) 49 | grpcServer.Serve(lis) 50 | return nil 51 | } 52 | func (g *Greeter) startREST() error { 53 | ctx := context.Background() 54 | ctx, cancel := context.WithCancel(ctx) 55 | defer cancel() 56 | 57 | mux := runtime.NewServeMux() 58 | opts := []grpc.DialOption{grpc.WithInsecure()} 59 | err := pb.RegisterGreeterHandlerFromEndpoint(ctx, mux, ":8080", opts) 60 | if err != nil { 61 | return err 62 | } 63 | 64 | return http.ListenAndServe(":8090", mux) 65 | } 66 | 67 | // SayHello says hello 68 | func (g *Greeter) SayHello(ctx context.Context, r *pb.HelloRequest) (*pb.HelloReply, error) { 69 | if err := r.Validate(); err != nil { 70 | return nil, err 71 | } 72 | return &pb.HelloReply{ 73 | Message: fmt.Sprintf("Hello, %s!", r.Name), 74 | }, nil 75 | } 76 | --------------------------------------------------------------------------------