service-template
Setup
Dependencies
Installation
# install docker
$ brew install docker
# install buf CLI
$ brew tap bufbuild/buf
$ brew install buf
# one time command to download proto dependencies
$ buf beta mod update
# generate client/swagger/server code from proto files
$ buf generate
Spin up the API
docker-compose up -d
Now you can test out curling your gRPC server via
grpcurl
List all gRPC services
$ grpcurl -plaintext localhost:8080 list
grpc.health.v1.Health
grpc.reflection.v1alpha.ServerReflection
template.AuthService
template.HealthService
template.TodoService
Describe a gRPC service
$ grpcurl -plaintext localhost:8080 describe template.AuthService
template.AuthService is a service:
service AuthService {
rpc Me ( .google.protobuf.Empty ) returns ( .template.User ) {
option (.google.api.http) = { get:"/auth/me" };
}
rpc Signup ( .template.SignupRequest ) returns ( .template.TokenResponse ) {
option (.google.api.http) = { post:"/auth/signup" body:"*" };
option (.grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { security:<> };
}
rpc Token ( .template.TokenRequest ) returns ( .template.TokenResponse ) {
option (.google.api.http) = { post:"/auth/token" body:"*" };
option (.grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { security:<> };
}
}
Auth
Create Account
$ grpcurl -plaintext \
-d '{ "email": "pep@test.com", "password": "mypass", "username": "pepsmooth", "given_name": "Pep", "family_name": "Smooth", "nickname": "Pep" }' \
localhost:8080 template.AuthService/Signup
{
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MjI4MzE3MjgsImp0aSI6IjA1NDcyOTU2LTI5YTctNGI4OS1hOGFjLTcyYjFmN2FhM2U5MiIsImlhdCI6MTYyMjgyODEyOCwiaXNzIjoiYXBpIiwic3ViIjoiMmQ5OTYyMDctYTkxMS00MDVlLWI1OTMtMTI1NjQzZmRiYzc4IiwiZW1haWwiOiJwZXBAdGVzdC5jb20iLCJ1c2VybmFtZSI6InBlcHNtb290aCIsIm5hbWUiOiJQZXAgU21vb3RoIiwiZ2l2ZW5fbmFtZSI6IlBlcCIsImZhbWlseV9uYW1lIjoiU21vb3RoIiwibmlja25hbWUiOiJQZXAiLCJwaWN0dXJlIjoiIn0.Zh35EwTtHMK0j0rLe_ZSt1eJZpy2lL11Ig0riPLUSfs",
"refreshToken": "NzQzNDE2NzUtMjU1Ni00M2VlLWE0NzItZTMxMjM4M2NjNzdi",
"tokenType": "bearer",
"expiresIn": 3600,
"refreshExpiresIn": 604800
}
Logged In User Details
$ grpcurl -plaintext \
-rpc-header "authorization:Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MjI4MzE3MjgsImp0aSI6IjA1NDcyOTU2LTI5YTctNGI4OS1hOGFjLTcyYjFmN2FhM2U5MiIsImlhdCI6MTYyMjgyODEyOCwiaXNzIjoiYXBpIiwic3ViIjoiMmQ5OTYyMDctYTkxMS00MDVlLWI1OTMtMTI1NjQzZmRiYzc4IiwiZW1haWwiOiJwZXBAdGVzdC5jb20iLCJ1c2VybmFtZSI6InBlcHNtb290aCIsIm5hbWUiOiJQZXAgU21vb3RoIiwiZ2l2ZW5fbmFtZSI6IlBlcCIsImZhbWlseV9uYW1lIjoiU21vb3RoIiwibmlja25hbWUiOiJQZXAiLCJwaWN0dXJlIjoiIn0.Zh35EwTtHMK0j0rLe_ZSt1eJZpy2lL11Ig0riPLUSfs" \
localhost:8080 template.AuthService/Me
{
"id": "05472956-29a7-4b89-a8ac-72b1f7aa3e92",
"email": "pep@test.com",
"username": "pepsmooth",
"givenName": "Pep",
"familyName": "Smooth",
"nickname": "Pep"
}
Login via Refresh Token
$ grpcurl -plaintext \
-d '{ "grant_type": "refresh_token", "username": "pepsmooth", "refresh_token": "NzQzNDE2NzUtMjU1Ni00M2VlLWE0NzItZTMxMjM4M2NjNzdi"}' \
localhost:8080 template.AuthService/Token
{
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MjI4MzE4ODEsImp0aSI6IjM1ODUyYjZlLTdlNmMtNDI4MS1hMzAxLTI3ZWY2YTk5NGY2YyIsImlhdCI6MTYyMjgyODI4MSwiaXNzIjoiYXBpIiwic3ViIjoiMmQ5OTYyMDctYTkxMS00MDVlLWI1OTMtMTI1NjQzZmRiYzc4IiwiZW1haWwiOiJwZXBAdGVzdC5jb20iLCJ1c2VybmFtZSI6InBlcHNtb290aCIsIm5hbWUiOiJQZXAgU21vb3RoIiwiZ2l2ZW5fbmFtZSI6IlBlcCIsImZhbWlseV9uYW1lIjoiU21vb3RoIiwibmlja25hbWUiOiJQZXAiLCJwaWN0dXJlIjoiIn0.VtdCVFwd2S9DJGTjmE0sYSWOYl9eZs1qg-5F9444m6M",
"refreshToken": "ZmZhNGU3MTMtY2YxOS00ODQ5LWE0YWUtOGU2NTBjMjliYWZm",
"tokenType": "bearer",
"expiresIn": 3600,
"refreshExpiresIn": 604800
}
Login via Password
$ grpcurl -plaintext \
-d '{ "grant_type": "password", "username": "pepsmooth", "password": "mypass"}' \
localhost:8080 template.AuthService/Token
{
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MjI4MzI1MTYsImp0aSI6IjFhYjk0YWEwLTJjOWMtNDY2NC1hODM5LWM2ZWY1ODYzM2RkNiIsImlhdCI6MTYyMjgyODkxNiwiaXNzIjoiYXBpIiwic3ViIjoiMmQ5OTYyMDctYTkxMS00MDVlLWI1OTMtMTI1NjQzZmRiYzc4IiwiZW1haWwiOiJwZXBAdGVzdC5jb20iLCJ1c2VybmFtZSI6InBlcHNtb290aCIsIm5hbWUiOiJQZXAgU21vb3RoIiwiZ2l2ZW5fbmFtZSI6IlBlcCIsImZhbWlseV9uYW1lIjoiU21vb3RoIiwibmlja25hbWUiOiJQZXAiLCJwaWN0dXJlIjoiIn0.esN_ipmbbITUlKSwQIo2rgbJHIe-1MTOsYNDcL1-K5o",
"refreshToken": "MWZmN2E3Y2EtNTE0Ni00Y2E3LTg2M2QtZWU5OTI4NGQzMTMy",
"tokenType": "bearer",
"expiresIn": 3600,
"refreshExpiresIn": 604800
}
Todos
Add a todo entry
$ grpcurl -plaintext \
-rpc-header "authorization:Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MjI4MzE3MjgsImp0aSI6IjA1NDcyOTU2LTI5YTctNGI4OS1hOGFjLTcyYjFmN2FhM2U5MiIsImlhdCI6MTYyMjgyODEyOCwiaXNzIjoiYXBpIiwic3ViIjoiMmQ5OTYyMDctYTkxMS00MDVlLWI1OTMtMTI1NjQzZmRiYzc4IiwiZW1haWwiOiJwZXBAdGVzdC5jb20iLCJ1c2VybmFtZSI6InBlcHNtb290aCIsIm5hbWUiOiJQZXAgU21vb3RoIiwiZ2l2ZW5fbmFtZSI6IlBlcCIsImZhbWlseV9uYW1lIjoiU21vb3RoIiwibmlja25hbWUiOiJQZXAiLCJwaWN0dXJlIjoiIn0.Zh35EwTtHMK0j0rLe_ZSt1eJZpy2lL11Ig0riPLUSfs" \
-d '{"text": "finish everything", "author": "pep"}' \
localhost:8080 template.TodoService/Create
{
"id": "6978690c-5a6d-4771-9182-81aaf6fc333e",
"text": "finish everything",
"author": "pep",
"timestamp": "2021-05-28 17:19:29.5131457 +0000 UTC m=+160.747979501"
}
List my todos
$ grpcurl -plaintext \
-rpc-header "authorization:Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MjI4MzE3MjgsImp0aSI6IjA1NDcyOTU2LTI5YTctNGI4OS1hOGFjLTcyYjFmN2FhM2U5MiIsImlhdCI6MTYyMjgyODEyOCwiaXNzIjoiYXBpIiwic3ViIjoiMmQ5OTYyMDctYTkxMS00MDVlLWI1OTMtMTI1NjQzZmRiYzc4IiwiZW1haWwiOiJwZXBAdGVzdC5jb20iLCJ1c2VybmFtZSI6InBlcHNtb290aCIsIm5hbWUiOiJQZXAgU21vb3RoIiwiZ2l2ZW5fbmFtZSI6IlBlcCIsImZhbWlseV9uYW1lIjoiU21vb3RoIiwibmlja25hbWUiOiJQZXAiLCJwaWN0dXJlIjoiIn0.Zh35EwTtHMK0j0rLe_ZSt1eJZpy2lL11Ig0riPLUSfs" \
localhost:8080 template.TodoService/ListAll
{
"todos": [
{
"id": "6978690c-5a6d-4771-9182-81aaf6fc333e",
"text": "finish everything",
"author": "pep",
"timestamp": "2021-05-28 17:19:29.5131457 +0000 UTC m=+160.747979501"
}
]
}
REST Gateway
######grpc-gateway reverse proxy will setup HTTP/1 endpoints for each gRPC method
Logged In User Details
$ curl -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MjI4MzI1MTYsImp0aSI6IjFhYjk0YWEwLTJjOWMtNDY2NC1hODM5LWM2ZWY1ODYzM2RkNiIsImlhdCI6MTYyMjgyODkxNiwiaXNzIjoiYXBpIiwic3ViIjoiMmQ5OTYyMDctYTkxMS00MDVlLWI1OTMtMTI1NjQzZmRiYzc4IiwiZW1haWwiOiJwZXBAdGVzdC5jb20iLCJ1c2VybmFtZSI6InBlcHNtb290aCIsIm5hbWUiOiJQZXAgU21vb3RoIiwiZ2l2ZW5fbmFtZSI6IlBlcCIsImZhbWlseV9uYW1lIjoiU21vb3RoIiwibmlja25hbWUiOiJQZXAiLCJwaWN0dXJlIjoiIn0.esN_ipmbbITUlKSwQIo2rgbJHIe-1MTOsYNDcL1-K5o" \
-X GET localhost:8080/auth/me | json_pp
{
"username" : "pepsmooth",
"nickname" : "Pep",
"id" : "1ab94aa0-2c9c-4664-a839-c6ef58633dd6",
"family_name" : "Smooth",
"email" : "pep@test.com",
"given_name" : "Pep"
}
List my todos
$ curl -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MjI4MzI1MTYsImp0aSI6IjFhYjk0YWEwLTJjOWMtNDY2NC1hODM5LWM2ZWY1ODYzM2RkNiIsImlhdCI6MTYyMjgyODkxNiwiaXNzIjoiYXBpIiwic3ViIjoiMmQ5OTYyMDctYTkxMS00MDVlLWI1OTMtMTI1NjQzZmRiYzc4IiwiZW1haWwiOiJwZXBAdGVzdC5jb20iLCJ1c2VybmFtZSI6InBlcHNtb290aCIsIm5hbWUiOiJQZXAgU21vb3RoIiwiZ2l2ZW5fbmFtZSI6IlBlcCIsImZhbWlseV9uYW1lIjoiU21vb3RoIiwibmlja25hbWUiOiJQZXAiLCJwaWN0dXJlIjoiIn0.esN_ipmbbITUlKSwQIo2rgbJHIe-1MTOsYNDcL1-K5o" \
-X GET localhost:8080/todos | json_pp
{
"todos": [
{
"id": "6978690c-5a6d-4771-9182-81aaf6fc333e",
"text": "finish everything",
"author": "pep",
"timestamp": "2021-05-28 17:19:29.5131457 +0000 UTC m=+160.747979501"
}
]
}
Swagger
Demo
Swagger
https://service-template-oxm27jqbha-uc.a.run.app/swagger-ui/
gRPC Health Endpoint
$ grpcurl service-template-oxm27jqbha-uc.a.run.app:443 \
template.HealthService/Readiness
{
"ok": true,
"ready": {
"datastore": true
}
}
REST Health Endpoint
$ curl -X GET https://service-template-oxm27jqbha-uc.a.run.app/readyz \
| json_pp
{
"ok" : true,
"ready" : {
"datastore" : true
}
}