Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[GSoD 2020] Added tutorials directory on gRPC-Gateway #1829

Merged
merged 19 commits into from
Dec 1, 2020
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/docs/faq.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
layout: default
title: FAQ
nav_order: 7
nav_order: 8
---

# FAQ
Expand Down
198 changes: 198 additions & 0 deletions docs/docs/tutorials/adding_annotations.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
---
layout: default
title: Adding the grpc-gateway annotations to an existing protobuf file
parent: Tutorials
nav_order: 5
---

# Adding the grpc-gateway annotations to an existing protobuf file

Now that we've got a working Go gRPC server, we need to add the grpc-gateway annotations.

The annotations define how gRPC services map to the JSON request and response. When using protocol buffers, each RPC must define the HTTP method and path using the `google.api.http` annotation.

So we will need to add the `google/api/http.proto` import to the proto file. We also need to add the HTTP->gRPC mapping we want. In this case, we're mapping `POST /v1/example/echo` to our `SayHello` rpc.

```proto
syntax = "proto3";

package helloworld;

import "google/api/annotations.proto";

// Here is the overall greeting service definition where we define all our endpoints
service Greeter {
// Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply) {
option (google.api.http) = {
post: "/v1/example/echo"
body: "*"
};
}
}

// The request message containing the user's name
message HelloRequest {
string name = 1;
}

// The response message containing the greetings
message HelloReply {
string message = 1;
}
```

johanbrandhorst marked this conversation as resolved.
Show resolved Hide resolved
iamrajiv marked this conversation as resolved.
Show resolved Hide resolved
See [a_bit_of_everything.proto](https://github.com/grpc-ecosystem/grpc-gateway/blob/master/examples/internal/proto/examplepb/a_bit_of_everything.proto) for examples of more annotations you can add to customize gateway behavior.

## Generating the grpc-gateway stubs

Now that we've got the grpc-gateway annotations added to the proto file, we need to use the grpc-gateway generator to generate the stubs.

Before we can do that, we need to copy some dependencies into our protofile structure. Copy the `third_party/googleapis` folder from the grpc-gateway repository to your local protofile structure. It should look like this afterwards:

```
proto
├── google
│ └── api
│ ├── annotations.proto
│ └── http.proto
└── helloworld
└── hello_world.proto
iamrajiv marked this conversation as resolved.
Show resolved Hide resolved
```

### Using buf

We'll need to add the grpc-gateway generator to the generation configuration:

```yml
version: v1beta1
plugins:
- name: go
out: proto
opt: paths=source_relative
- name: go-grpc
out: proto
opt: paths=source_relative,require_unimplemented_servers=false
- name: grpc-gateway
out: proto
opt: paths=source_relative
```

And that's it! Now if you run:

```sh
$ buf generate
```

It should produce a `*.gw.pb.go` file.

### Using protoc

Now we need to add the grpc-gateway generator to the protoc invocation:

```sh
$ protoc -I ./proto \
--go_out ./proto --go_opt paths=source_relative \
--go-grpc_out ./proto --go-grpc_opt paths=source_relative \
--grpc-gateway_out ./proto --grpc-gateway_opt paths=source_relative \
./proto/helloworld/hello_world.proto
```

This should generate a `*.gw.pb.go` file.

johanbrandhorst marked this conversation as resolved.
Show resolved Hide resolved
We also need to add and serve the gRPC-gateway mux in our `main.go` file.

```go
package main

import (
"context"
"log"
"net"
"net/http"

"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
"google.golang.org/grpc"

helloworldpb "github.com/myuser/myrepo/proto/helloworld"
)

type server struct{}

func NewServer() *server {
return &server{}
}

func (s *server) SayHello(ctx context.Context, in *helloworldpb.HelloRequest) (*helloworldpb.HelloReply, error) {
return &helloworldpb.HelloReply{Message: in.Name + " World"}, nil
}

func main() {
// Create a listener on TCP port
lis, err := net.Listen("tcp", ":8080")
if err != nil {
log.Fatalln("Failed to listen:", err)
}

// Create a gRPC server object
s := grpc.NewServer()
// Attach the Greeter service to the server
helloworldpb.RegisterGreeterServer(s, &server{})
// Serve gRPC Server
log.Println("Serving gRPC on 0.0.0.0:8080")
go func() {
log.Fatalln(s.Serve(lis))
}()

// Create a client connection to the gRPC Server we just started
// This is where the gRPC-Gateway proxies the requests
conn, err := grpc.DialContext(
context.Background(),
"0.0.0.0:8080",
grpc.WithBlock(),
grpc.WithInsecure(),
)
if err != nil {
log.Fatalln("Failed to dial server:", err)
}

gwmux := runtime.NewServeMux()
// Register Greeter
err = helloworldpb.RegisterGreeterHandler(context.Background(), gwmux, conn)
if err != nil {
log.Fatalln("Failed to register gateway:", err)
}

gwServer := &http.Server{
Addr: ":8090",
Handler: gwmux,
}

log.Println("Serving gRPC-Gateway on http://0.0.0.0:8090")
log.Fatalln(gwServer.ListenAndServe())
}
```

For more examples, please refer to [our boilerplate repository](https://github.com/johanbrandhorst/grpc-gateway-boilerplate).

## Testing the gRPC-Gateway

Now we can start the server:

```sh
$ go run main.go
```

Then we use cURL to send HTTP requests:

```sh
$ curl -X POST -k http://localhost:8090/v1/example/echo -d '{"name": " Hello"}'
```

```
{"message":"Hello World"}
```

Hopefully, that gives a bit of understanding of how to use the gRPC-Gateway.

[Next](learn_more.md){: .btn .btn-primary .fs-5 .mb-4 .mb-md-0 .mr-2 }
56 changes: 56 additions & 0 deletions docs/docs/tutorials/creating_main.go.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
---
layout: default
title: Creating main.go
parent: Tutorials
nav_order: 4
---

# Creating main.go

Before creating `main.go` file we are assuming that the user has created a `go.mod` with the name `github.com/myuser/myrepo`, if not please refer to [Creating go.mod file](introduction.md#creating-gomod-file). The import here is using the path to the generated files in `proto/helloworld` relative to the root of the repository.

```go
package main

import (
"context"
"log"
"net"

"google.golang.org/grpc"

helloworldpb "github.com/myuser/myrepo/proto/helloworld"
)

type server struct{}

func NewServer() *server {
return &server{}
}

func (s *server) SayHello(ctx context.Context, in *helloworldpb.HelloRequest) (*helloworldpb.HelloReply, error) {
return &helloworldpb.HelloReply{Message: in.Name + " World"}, nil
}

func main() {
// Create a listener on TCP port
lis, err := net.Listen("tcp", ":8080")
johanbrandhorst marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
log.Fatalln("Failed to listen:", err)
}

// Create a gRPC server object
s := grpc.NewServer()
// Attach the Greeter service to the server
helloworldpb.RegisterGreeterServer(s, &server{})
// Serve gRPC Server
log.Println("Serving gRPC on 0.0.0.0:8080")
log.Fatal(s.Serve(lis))
}
```

## Read More

For more refer to gRPC docs [https://grpc.io/docs/languages/go/](https://grpc.io/docs/languages/go/).

[Next](adding_annotations.md){: .btn .btn-primary .fs-5 .mb-4 .mb-md-0 .mr-2 }
9 changes: 9 additions & 0 deletions docs/docs/tutorials/generating_stubs/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
layout: default
title: Generating stubs
parent: Tutorials
nav_order: 3
has_children: true
---
iamrajiv marked this conversation as resolved.
Show resolved Hide resolved

For generating the stubs, we have two alternatives: `protoc` and `buf`. `protoc` is the more classic generation experience that is used widely in the industry, but it has a pretty steep learning curve. `buf` is a newer tool that is built with user experience and speed in mind. It also offers linting and breaking change detection, something `protoc` doesn't offer. We offer instructions for both here.
49 changes: 49 additions & 0 deletions docs/docs/tutorials/generating_stubs/using_buf.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
---
layout: default
title: Generating stubs using buf
parent: Generating stubs
grand_parent: Tutorials
nav_order: 1
---

# Generating stubs using buf

[Buf](https://github.com/bufbuild/buf) is a tool that provides various protobuf utilities such as linting, breaking change detection and generation. Please find installation instructions on [https://docs.buf.build/installation/](https://docs.buf.build/installation/).

It is configured through a `buf.yaml` file that should be checked in to the root of your repository. Buf will automatically read this file if present. Configuration can also be provided via the command-line flag `--config`, which accepts a path to a `.json` or `.yaml` file, or direct JSON or YAML data.

All Buf operations that use your local `.proto` files as input rely on a valid build configuration. This configuration tells Buf where to search for `.proto` files, and how to handle imports. As opposed to `protoc`, where all `.proto` files are manually specified on the command-line, buf operates by recursively discovering all `.proto` files under configuration and building them.

The following is an example of a valid configuration, assuming you have your `.proto` files rooted in the `proto` folder relative to the root of your repository.

```yml
version: v1beta1
build:
roots:
- proto
```

To generate type and gRPC stubs for Go, create the file `buf.gen.yaml` at the root of the repository:

```yml
version: v1beta1
plugins:
- name: go
out: proto
opt: paths=source_relative
- name: go-grpc
out: proto
opt: paths=source_relative
```

We use the `go` and `go-grpc` plugins to generate Go types and gRPC service definitions. We're outputting the generated files relative to the `proto` folder, and we're using the `paths=source_relative` option, which means that the generated files will appear in the same directory as the source `.proto` file.

Then run

```sh
$ buf generate
```

This will have generated a `*.pb.go` and a `*_grpc.pb.go` file for each protobuf package in our `proto` file hierarchy.

[Next](using_protoc.md){: .btn .btn-primary .fs-5 .mb-4 .mb-md-0 .mr-2 }
iamrajiv marked this conversation as resolved.
Show resolved Hide resolved
24 changes: 24 additions & 0 deletions docs/docs/tutorials/generating_stubs/using_protoc.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
---
layout: default
title: Generating stubs using protoc
parent: Generating stubs
grand_parent: Tutorials
nav_order: 2
---

# Generating stubs using protoc

Here's an example of what a `protoc` command might look like to generate Go stubs, assuming that you're at the root of your repository and you have your proto files in a directory called `proto`:

```sh
$ protoc -I ./proto \
--go_out ./proto --go_opt paths=source_relative \
--go-grpc_out ./proto --go-grpc_opt paths=source_relative \
./proto/helloworld/hello_world.proto
```

We use the `go` and `go-grpc` plugins to generate Go types and gRPC service definitions. We're outputting the generated files relative to the `proto` folder, and we're using the `paths=source_relative` option, which means that the generated files will appear in the same directory as the source `.proto` file.

This will have generated a `*.pb.go` and a `*_grpc.pb.go` file for `proto/helloworld/hello_world.proto`.

[Next](../creating_main.go.md){: .btn .btn-primary .fs-5 .mb-4 .mb-md-0 .mr-2 }
6 changes: 6 additions & 0 deletions docs/docs/tutorials/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
layout: default
title: Tutorials
nav_order: 7
has_children: true
---
Loading