README.md
1# tonic-types
2
3A collection of useful protobuf types that can be used with `tonic`.
4
5This crate also introduces the [`StatusExt`] trait and implements it in
6[`tonic::Status`], allowing the implementation of the [gRPC Richer Error Model]
7with [`tonic`] in a convenient way.
8
9## Usage
10
11Useful protobuf types are available through the [`pb`] module. They can be
12imported and worked with directly.
13
14The [`StatusExt`] trait adds associated functions to [`tonic::Status`] that can
15be used on the server side to create a status with error details, which can then
16be returned to gRPC clients. Moreover, the trait also adds methods to
17[`tonic::Status`] that can be used by a tonic client to extract error details,
18and handle them with ease.
19
20## Examples
21
22The examples bellow cover a basic use case of the [gRPC Richer Error Model].
23More complete server and client implementations are provided in the
24**Richer Error example**, located in the main repo [examples] directory.
25
26### Server Side: Generating [`tonic::Status`] with an [`ErrorDetails`] struct
27
28```rust
29use tonic::{Code, Status};
30use tonic_types::{ErrorDetails, StatusExt};
31
32// ...
33// Inside a gRPC server endpoint that returns `Result<Response<T>, Status>`
34
35// Create empty `ErrorDetails` struct
36let mut err_details = ErrorDetails::new();
37
38// Add error details conditionally
39if some_condition {
40 err_details.add_bad_request_violation(
41 "field_a",
42 "description of why the field_a is invalid"
43 );
44}
45
46if other_condition {
47 err_details.add_bad_request_violation(
48 "field_b",
49 "description of why the field_b is invalid",
50 );
51}
52
53// Check if any error details were set and return error status if so
54if err_details.has_bad_request_violations() {
55 // Add additional error details if necessary
56 err_details
57 .add_help_link("description of link", "https://resource.example.local")
58 .set_localized_message("en-US", "message for the user");
59
60 let status = Status::with_error_details(
61 Code::InvalidArgument,
62 "bad request",
63 err_details,
64 );
65 return Err(status);
66}
67
68// Handle valid request
69// ...
70```
71
72### Client Side: Extracting an [`ErrorDetails`] struct from [`tonic::Status`]
73
74```rust
75use tonic::{Response, Status};
76use tonic_types::StatusExt;
77
78// ...
79// Where `req_result` was returned by a gRPC client endpoint method
80fn handle_request_result<T>(req_result: Result<Response<T>, Status>) {
81 match req_result {
82 Ok(response) => {
83 // Handle successful response
84 },
85 Err(status) => {
86 let err_details = status.get_error_details();
87 if let Some(bad_request) = err_details.bad_request() {
88 // Handle bad_request details
89 }
90 if let Some(help) = err_details.help() {
91 // Handle help details
92 }
93 if let Some(localized_message) = err_details.localized_message() {
94 // Handle localized_message details
95 }
96 }
97 };
98}
99```
100
101## Working with different error message types
102
103Multiple examples are provided at the [`ErrorDetails`] doc. Instructions about
104how to use the fields of the standard error message types correctly are provided
105at [error_details.proto].
106
107## Alternative `tonic::Status` associated functions and methods
108
109In the [`StatusExt`] doc, an alternative way of interacting with
110[`tonic::Status`] is presented, using vectors of error details structs wrapped
111with the [`ErrorDetail`] enum. This approach can provide more control over the
112vector of standard error messages that will be generated or that was received,
113if necessary. To see how to adopt this approach, please check the
114[`StatusExt::with_error_details_vec`] and [`StatusExt::get_error_details_vec`]
115docs, and also the main repo's [Richer Error example] directory.
116
117Besides that, multiple examples with alternative error details extraction
118methods are provided in the [`StatusExt`] doc, which can be specially
119useful if only one type of standard error message is being handled by the
120client. For example, using [`StatusExt::get_details_bad_request`] is a
121more direct way of extracting a [`BadRequest`] error message from
122[`tonic::Status`].
123
124[`tonic::Status`]: https://docs.rs/tonic/latest/tonic/struct.Status.html
125[`tonic`]: https://docs.rs/tonic/latest/tonic/
126[gRPC Richer Error Model]: https://www.grpc.io/docs/guides/error/
127[`pb`]: https://docs.rs/tonic-types/latest/tonic_types/pb/index.html
128[`StatusExt`]: https://docs.rs/tonic-types/latest/tonic_types/trait.StatusExt.html
129[examples]: https://github.com/hyperium/tonic/tree/master/examples
130[`ErrorDetails`]: https://docs.rs/tonic-types/latest/tonic_types/struct.ErrorDetails.html
131[error_details.proto]: https://github.com/googleapis/googleapis/blob/master/google/rpc/error_details.proto
132[`ErrorDetail`]: https://docs.rs/tonic-types/latest/tonic_types/enum.ErrorDetail.html
133[`StatusExt::with_error_details_vec`]: https://docs.rs/tonic-types/latest/tonic_types/trait.StatusExt.html#tymethod.with_error_details_vec
134[`StatusExt::get_error_details_vec`]: https://docs.rs/tonic-types/latest/tonic_types/trait.StatusExt.html#tymethod.get_error_details_vec
135[Richer Error example]: https://github.com/hyperium/tonic/tree/master/examples/src/richer-error
136[`StatusExt::get_details_bad_request`]: https://docs.rs/tonic-types/latest/tonic_types/trait.StatusExt.html#tymethod.get_details_bad_request
137[`BadRequest`]: https://docs.rs/tonic-types/latest/tonic_types/struct.BadRequest.html