gRPC Services

Define gRPC services with RPCs, messages, and Protocol Buffer schemas

gRPC services are the core building blocks of your API. Each service defines a collection of remote procedure calls (RPCs) that clients can invoke, along with the message types used for requests and responses.

Service Definition

Define a gRPC service in a .proto file:

user_service.proto
1syntax = "proto3";
2
3package userservice.v1;
4
5import "google/protobuf/empty.proto";
6import "google/protobuf/timestamp.proto";
7import "google/protobuf/field_mask.proto";
8
9// User management service
10service UserService {
11 // Create a new user account
12 rpc CreateUser(CreateUserRequest) returns (User) {
13 option deprecated = false;
14 }
15
16 // Get user by ID
17 rpc GetUser(GetUserRequest) returns (User);
18
19 // Update user information
20 rpc UpdateUser(UpdateUserRequest) returns (User);
21
22 // Delete a user account
23 rpc DeleteUser(DeleteUserRequest) returns (google.protobuf.Empty);
24
25 // List users with pagination
26 rpc ListUsers(ListUsersRequest) returns (ListUsersResponse);
27
28 // Search users by various criteria
29 rpc SearchUsers(SearchUsersRequest) returns (SearchUsersResponse);
30}
31
32// User message definition
33message User {
34 string id = 1;
35 string email = 2;
36 string name = 3;
37 int32 age = 4;
38 UserStatus status = 5;
39 google.protobuf.Timestamp created_at = 6;
40 google.protobuf.Timestamp updated_at = 7;
41 repeated string roles = 8;
42 UserPreferences preferences = 9;
43}
44
45// User status enumeration
46enum UserStatus {
47 USER_STATUS_UNSPECIFIED = 0;
48 USER_STATUS_ACTIVE = 1;
49 USER_STATUS_INACTIVE = 2;
50 USER_STATUS_SUSPENDED = 3;
51 USER_STATUS_DELETED = 4;
52}
53
54// Nested message for user preferences
55message UserPreferences {
56 bool email_notifications = 1;
57 string timezone = 2;
58 string language = 3;
59 ThemeMode theme = 4;
60}
61
62enum ThemeMode {
63 THEME_MODE_UNSPECIFIED = 0;
64 THEME_MODE_LIGHT = 1;
65 THEME_MODE_DARK = 2;
66 THEME_MODE_AUTO = 3;
67}

Request and Response Messages

Define clear request and response message types:

user_messages.proto
1// Create user request
2message CreateUserRequest {
3 string email = 1 [(validate.rules).string.email = true];
4 string name = 2 [(validate.rules).string.min_len = 1];
5 int32 age = 3 [(validate.rules).int32.gte = 0];
6 UserPreferences preferences = 4;
7}
8
9// Get user request
10message GetUserRequest {
11 string id = 1 [(validate.rules).string.uuid = true];
12}
13
14// Update user request
15message UpdateUserRequest {
16 string id = 1 [(validate.rules).string.uuid = true];
17 User user = 2;
18 google.protobuf.FieldMask update_mask = 3;
19}
20
21// Delete user request
22message DeleteUserRequest {
23 string id = 1 [(validate.rules).string.uuid = true];
24}
25
26// List users request with pagination
27message ListUsersRequest {
28 int32 page_size = 1 [(validate.rules).int32 = {gte: 1, lte: 100}];
29 string page_token = 2;
30 string filter = 3;
31 string order_by = 4;
32}
33
34// List users response
35message ListUsersResponse {
36 repeated User users = 1;
37 string next_page_token = 2;
38 int32 total_count = 3;
39}
40
41// Search users request
42message SearchUsersRequest {
43 string query = 1 [(validate.rules).string.min_len = 1];
44 repeated UserStatus status_filter = 2;
45 repeated string role_filter = 3;
46 int32 page_size = 4 [(validate.rules).int32 = {gte: 1, lte: 100}];
47 string page_token = 5;
48}
49
50// Search users response
51message SearchUsersResponse {
52 repeated User users = 1;
53 string next_page_token = 2;
54 int32 total_count = 3;
55 SearchMetadata metadata = 4;
56}
57
58message SearchMetadata {
59 int32 search_time_ms = 1;
60 repeated string suggested_corrections = 2;
61}

Service Implementation

Implement the service in your preferred language:

user_service.py
1import grpc
2from grpc import ServicerContext
3import user_service_pb2
4import user_service_pb2_grpc
5from google.protobuf import empty_pb2
6from typing import Iterator
7
8class UserServiceServicer(user_service_pb2_grpc.UserServiceServicer):
9
10 def __init__(self, user_repository):
11 self.user_repository = user_repository
12
13 def CreateUser(
14 self,
15 request: user_service_pb2.CreateUserRequest,
16 context: ServicerContext
17 ) -> user_service_pb2.User:
18 """Create a new user account."""
19 try:
20 # Validate request
21 if not request.email or not request.name:
22 context.set_code(grpc.StatusCode.INVALID_ARGUMENT)
23 context.set_details('Email and name are required')
24 return user_service_pb2.User()
25
26 # Check if user already exists
27 if self.user_repository.get_by_email(request.email):
28 context.set_code(grpc.StatusCode.ALREADY_EXISTS)
29 context.set_details(f'User with email {request.email} already exists')
30 return user_service_pb2.User()
31
32 # Create user
33 user = self.user_repository.create_user(
34 email=request.email,
35 name=request.name,
36 age=request.age,
37 preferences=request.preferences
38 )
39
40 return user
41
42 except Exception as e:
43 context.set_code(grpc.StatusCode.INTERNAL)
44 context.set_details(f'Failed to create user: {str(e)}')
45 return user_service_pb2.User()
46
47 def GetUser(
48 self,
49 request: user_service_pb2.GetUserRequest,
50 context: ServicerContext
51 ) -> user_service_pb2.User:
52 """Get user by ID."""
53 try:
54 user = self.user_repository.get_by_id(request.id)
55 if not user:
56 context.set_code(grpc.StatusCode.NOT_FOUND)
57 context.set_details(f'User with ID {request.id} not found')
58 return user_service_pb2.User()
59
60 return user
61
62 except Exception as e:
63 context.set_code(grpc.StatusCode.INTERNAL)
64 context.set_details(f'Failed to get user: {str(e)}')
65 return user_service_pb2.User()
66
67 def UpdateUser(
68 self,
69 request: user_service_pb2.UpdateUserRequest,
70 context: ServicerContext
71 ) -> user_service_pb2.User:
72 """Update user information."""
73 try:
74 # Check if user exists
75 existing_user = self.user_repository.get_by_id(request.id)
76 if not existing_user:
77 context.set_code(grpc.StatusCode.NOT_FOUND)
78 context.set_details(f'User with ID {request.id} not found')
79 return user_service_pb2.User()
80
81 # Apply field mask for partial updates
82 updated_user = self.user_repository.update_user(
83 user_id=request.id,
84 updates=request.user,
85 field_mask=request.update_mask
86 )
87
88 return updated_user
89
90 except Exception as e:
91 context.set_code(grpc.StatusCode.INTERNAL)
92 context.set_details(f'Failed to update user: {str(e)}')
93 return user_service_pb2.User()
94
95 def DeleteUser(
96 self,
97 request: user_service_pb2.DeleteUserRequest,
98 context: ServicerContext
99 ) -> empty_pb2.Empty:
100 """Delete a user account."""
101 try:
102 # Check if user exists
103 user = self.user_repository.get_by_id(request.id)
104 if not user:
105 context.set_code(grpc.StatusCode.NOT_FOUND)
106 context.set_details(f'User with ID {request.id} not found')
107 return empty_pb2.Empty()
108
109 # Soft delete user
110 self.user_repository.delete_user(request.id)
111
112 return empty_pb2.Empty()
113
114 except Exception as e:
115 context.set_code(grpc.StatusCode.INTERNAL)
116 context.set_details(f'Failed to delete user: {str(e)}')
117 return empty_pb2.Empty()
118
119 def ListUsers(
120 self,
121 request: user_service_pb2.ListUsersRequest,
122 context: ServicerContext
123 ) -> user_service_pb2.ListUsersResponse:
124 """List users with pagination."""
125 try:
126 # Apply pagination
127 page_size = min(request.page_size or 20, 100)
128
129 users, next_page_token, total_count = self.user_repository.list_users(
130 page_size=page_size,
131 page_token=request.page_token,
132 filter_expr=request.filter,
133 order_by=request.order_by
134 )
135
136 return user_service_pb2.ListUsersResponse(
137 users=users,
138 next_page_token=next_page_token,
139 total_count=total_count
140 )
141
142 except Exception as e:
143 context.set_code(grpc.StatusCode.INTERNAL)
144 context.set_details(f'Failed to list users: {str(e)}')
145 return user_service_pb2.ListUsersResponse()
146
147 def SearchUsers(
148 self,
149 request: user_service_pb2.SearchUsersRequest,
150 context: ServicerContext
151 ) -> user_service_pb2.SearchUsersResponse:
152 """Search users by various criteria."""
153 try:
154 start_time = time.time()
155
156 users, next_page_token, total_count = self.user_repository.search_users(
157 query=request.query,
158 status_filter=request.status_filter,
159 role_filter=request.role_filter,
160 page_size=request.page_size or 20,
161 page_token=request.page_token
162 )
163
164 search_time_ms = int((time.time() - start_time) * 1000)
165
166 metadata = user_service_pb2.SearchMetadata(
167 search_time_ms=search_time_ms,
168 suggested_corrections=[] # Add spell check suggestions if needed
169 )
170
171 return user_service_pb2.SearchUsersResponse(
172 users=users,
173 next_page_token=next_page_token,
174 total_count=total_count,
175 metadata=metadata
176 )
177
178 except Exception as e:
179 context.set_code(grpc.StatusCode.INTERNAL)
180 context.set_details(f'Failed to search users: {str(e)}')
181 return user_service_pb2.SearchUsersResponse()

Protocol Buffer Best Practices

Field Numbers

  • Use field numbers 1-15 for frequently used fields (more efficient encoding)
  • Reserve field numbers for removed fields to maintain compatibility
  • Never reuse field numbers
1message User {
2 // Frequently used fields (1-15)
3 string id = 1;
4 string email = 2;
5 string name = 3;
6
7 // Less frequently used fields
8 UserPreferences preferences = 16;
9 repeated string tags = 17;
10
11 // Reserved fields
12 reserved 4, 5, 6;
13 reserved "old_field_name", "deprecated_field";
14}

Naming Conventions

  • Use snake_case for field names
  • Use PascalCase for message and service names
  • Use UPPER_SNAKE_CASE for enum values
1service UserManagementService { // PascalCase
2 rpc GetUser(GetUserRequest) returns (User);
3}
4
5message User { // PascalCase
6 string first_name = 1; // snake_case
7 UserStatus status = 2;
8}
9
10enum UserStatus {
11 USER_STATUS_UNSPECIFIED = 0; // UPPER_SNAKE_CASE
12 USER_STATUS_ACTIVE = 1;
13}

Versioning

  • Include version in package names
  • Use semantic versioning for breaking changes
1syntax = "proto3";
2
3package userservice.v1; // Version in package name
4
5option go_package = "example.com/userservice/v1";

Multiple Services

Organize related functionality into separate services:

services.proto
1// User management
2service UserService {
3 rpc CreateUser(CreateUserRequest) returns (User);
4 rpc GetUser(GetUserRequest) returns (User);
5}
6
7// Authentication
8service AuthService {
9 rpc Login(LoginRequest) returns (LoginResponse);
10 rpc RefreshToken(RefreshTokenRequest) returns (RefreshTokenResponse);
11}
12
13// Notification service
14service NotificationService {
15 rpc SendNotification(SendNotificationRequest) returns (SendNotificationResponse);
16 rpc GetNotificationPreferences(GetNotificationPreferencesRequest) returns (NotificationPreferences);
17}

gRPC services provide a strongly-typed, high-performance foundation for building distributed systems with clear contracts between clients and servers.