Before you start
Make sure that you can compile and run core tests and read a basic example Writing your first HTTP server.
Step by step guide
In this example, we will write a client side and a server side for a simple GreeterService from greeter.proto (see the schema below). Its single SayHello method accepts a name string and replies with a corresponding greeting string.
Installation
We generate and link to a CMake library from our .proto schema:
include(GrpcTargets)
userver_add_grpc_library(${PROJECT_NAME}-proto PROTOS samples/greeter.proto)
target_link_libraries(${PROJECT_NAME} ${PROJECT_NAME}-proto)
Register the necessary ugrpc components:
          .Append<ugrpc::client::ClientFactoryComponent>()
 
The client side
Wrap the generated api::GreeterServiceClient in a component that exposes a simplified interface:
 public:
  static constexpr std::string_view kName = "greeter-client";
 
      : LoggableComponentBase(config, context),
        
        client_factory_(
            context.FindComponent<
ugrpc::client::ClientFactoryComponent>()
 
                .GetFactory()),
        
        client_(client_factory_.MakeClient<api::GreeterServiceClient>(
            "greeter", config[
"endpoint"].As<
std::string>())) {}
 
 
  std::string SayHello(std::string name);
 
 
 private:
  api::GreeterServiceClient client_;
};
 A single request-response RPC handling is simple: fill in request and context, initiate the RPC, receive the response.
std::string GreeterClient::SayHello(std::string name) {
  api::GreetingRequest request;
  request.set_name(std::move(name));
 
  
  auto context = std::make_unique<grpc::ClientContext>();
  context->set_deadline(
 
  
  
  auto stream = client_.SayHello(request, std::move(context));
 
  
  
  
  
  api::GreetingResponse response = stream.Finish();
 
  return std::move(*response.mutable_greeting());
}
Fill in the static config entries for the client side:
# yaml
        # Creates gRPC clients
        grpc-client-factory:
            # The TaskProcessor for blocking connection initiation
            task-processor: grpc-blocking-task-processor
 
            # Optional channel parameters for gRPC Core
            # https://grpc.github.io/grpc/core/group__grpc__arg__keys.html
            channel-args: {}
 
        # Our wrapper around the generated client for GreeterService
        greeter-client:
            # The service endpoint (URI). We talk to our own service,
            # which is kind of pointless, but works for an example
            endpoint: '[::1]:8091'
# yaml
    task_processors:
        grpc-blocking-task-processor:  # For blocking gRPC channel creation
            worker_threads: 2
            thread_name: grpc-worker
The server side
Implement the generated api::GreeterServiceBase. As a convenience, a derived api::GreeterServiceBase::Component class is provided for easy integration with the component system.
class GreeterServiceComponent final
    : public api::GreeterServiceBase::Component {
 public:
  static constexpr std::string_view kName = "greeter-service";
 
      : api::GreeterServiceBase::Component(config, context),
        prefix_(config[
"greeting-prefix"].As<
std::string>()) {}
 
 
  void SayHello(SayHelloCall& call, api::GreetingRequest&& request) override;
 
 
 private:
  const std::string prefix_;
};
A single request-response RPC handling is simple: fill in the response and send it.
void GreeterServiceComponent::SayHello(
    api::GreeterServiceBase::SayHelloCall& call,
    api::GreetingRequest&& request) {
  
  
 
  api::GreetingResponse response;
  response.set_greeting(fmt::format("{}, {}!", prefix_, request.name()));
 
  
  
  
  call.Finish(response);
}
Fill in the static config entries for the server side:
# yaml
        # Common configuration for gRPC server
        grpc-server:
            # The single listening port for incoming RPCs
            port: $grpc_server_port
            port#fallback: 8091
            completion-queue-count: 3
 
        # Our GreeterService implementation
        greeter-service:
            task-processor: main-task-processor
            greeting-prefix: Hello
            middlewares: []
int main()
Finally, we register our components and start the server.
int main(int argc, char* argv[]) {
  const auto component_list =
          .
Append<samples::http_cache::HttpCachedTranslations>()
 
          .Append<samples::http_cache::GreetUser>()
          .Append<server::handlers::TestsControl>()
          .Append<components::HttpClient>();
}
Build and Run
To build the sample, execute the following build steps at the userver root directory:
mkdir build_release
cd build_release
cmake -DCMAKE_BUILD_TYPE=Release ..
make userver-samples-grpc_service
The sample could be started by running make start-userver-samples-grpc_service. The command would invoke testsuite start target that sets proper paths in the configuration files and starts the service.
To start the service manually run ./samples/grpc_service/userver-samples-grpc_service -c </path/to/static_config.yaml>.
The service is available locally at port 8091 (as per our static_config.yaml).
Functional testing
To implement Functional tests for the service some preparational steps should be done.
Preparations
First of all, import the required modules and add the required pytest_userver.plugins.grpc pytest plugin:
import pytest
import samples.greeter_pb2_grpc as greeter_services  
 
pytest_plugins = ['pytest_userver.plugins.grpc']
gRPC server mock
To mock the gRPC server provide a hook for the static config to change the endpoint:
USERVER_CONFIG_HOOKS = ['prepare_service_config']
 
 
@pytest.fixture(scope='session')
def prepare_service_config(grpc_mockserver_endpoint):
    def patch_config(config, config_vars):
        components = config['components_manager']['components']
        components['greeter-client']['endpoint'] = grpc_mockserver_endpoint
 
    return patch_config
Write the mocking fixtures using grpc_mockserver:
@pytest.fixture(name='mock_grpc_greeter_session', scope='session')
def _mock_grpc_greeter_session(grpc_mockserver, create_grpc_mock):
    mock = create_grpc_mock(greeter_services.GreeterServiceServicer)
    greeter_services.add_GreeterServiceServicer_to_server(
        mock.servicer, grpc_mockserver,
    )
    return mock
 
 
@pytest.fixture
def mock_grpc_greeter(mock_grpc_greeter_session):
    with mock_grpc_greeter_session.mock() as mock:
        yield mock
After that everything is ready to check the service client requests:
async def test_grpc_client(service_client, mock_grpc_greeter):
    @mock_grpc_greeter('SayHello')
    async def _mock_say_hello(request, context):
        return greeter_protos.GreetingResponse(
            greeting=f'Hello, {request.name} from mockserver!',
        )
 
    response = await service_client.post(
        '/hello', data='tests', headers={'Content-type': 'text/plain'},
    )
    assert response.status == 200
    assert response.content == b'Hello, tests from mockserver!'
 
    assert _mock_say_hello.times_called == 1
gRPC client
To do the gRPC requests write a client fixture using grpc_channel:
@pytest.fixture
def grpc_client(grpc_channel, service_client):
    return greeter_services.GreeterServiceStub(grpc_channel)
Use it to do gRPC requests to the service:
async def test_say_hello(grpc_client):
    request = greeter_protos.GreetingRequest(name='Python')
    response = await grpc_client.SayHello(request)
    assert response.greeting == 'Hello, Python!'
Full sources
See the full example at: