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
1 syntax = "proto3"; 2 3 package userservice.v1; 4 5 import "google/protobuf/empty.proto"; 6 import "google/protobuf/timestamp.proto"; 7 import "google/protobuf/field_mask.proto"; 8 9 // User management service 10 service 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 33 message 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 46 enum 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 55 message UserPreferences { 56 bool email_notifications = 1; 57 string timezone = 2; 58 string language = 3; 59 ThemeMode theme = 4; 60 } 61 62 enum 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 2 message 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 10 message GetUserRequest { 11 string id = 1 [(validate.rules).string.uuid = true]; 12 } 13 14 // Update user request 15 message 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 22 message DeleteUserRequest { 23 string id = 1 [(validate.rules).string.uuid = true]; 24 } 25 26 // List users request with pagination 27 message 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 35 message ListUsersResponse { 36 repeated User users = 1; 37 string next_page_token = 2; 38 int32 total_count = 3; 39 } 40 41 // Search users request 42 message 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 51 message SearchUsersResponse { 52 repeated User users = 1; 53 string next_page_token = 2; 54 int32 total_count = 3; 55 SearchMetadata metadata = 4; 56 } 57 58 message 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
1 import grpc 2 from grpc import ServicerContext 3 import user_service_pb2 4 import user_service_pb2_grpc 5 from google.protobuf import empty_pb2 6 from typing import Iterator 7 8 class 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
1 message 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
1 service UserManagementService { // PascalCase 2 rpc GetUser(GetUserRequest) returns (User); 3 } 4 5 message User { // PascalCase 6 string first_name = 1; // snake_case 7 UserStatus status = 2; 8 } 9 10 enum 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
1 syntax = "proto3"; 2 3 package userservice.v1; // Version in package name 4 5 option go_package = "example.com/userservice/v1";
Multiple Services
Organize related functionality into separate services:
services.proto
1 // User management 2 service UserService { 3 rpc CreateUser(CreateUserRequest) returns (User); 4 rpc GetUser(GetUserRequest) returns (User); 5 } 6 7 // Authentication 8 service AuthService { 9 rpc Login(LoginRequest) returns (LoginResponse); 10 rpc RefreshToken(RefreshTokenRequest) returns (RefreshTokenResponse); 11 } 12 13 // Notification service 14 service 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.