Skip to content

Commit 171645d

Browse files
feat(examples): add advanced client usage examples
Add three new example files demonstrating advanced client features: - client_error_handling.go: Comprehensive error handling patterns - ValidationError with field-level violations - Generic API errors - Network errors and timeouts - Context cancellation - client_content_types.go: JSON vs binary protobuf - Default JSON client - Binary protobuf for performance - Per-request content type override - Performance comparison - client_per_call_options.go: Per-request customization - Custom headers (tracing, correlation IDs) - Generated header helpers - Dynamic headers based on context - A/B testing with headers Update README.md with documentation for all client examples. Co-Authored-By: Claude Opus 4.5 <[email protected]>
1 parent 10ff54b commit 171645d

File tree

4 files changed

+589
-0
lines changed

4 files changed

+589
-0
lines changed

examples/restful-crud/README.md

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,3 +220,88 @@ Path parameters like `{product_id}` are automatically bound from the URL path to
220220
### Query Parameters
221221

222222
Fields annotated with `(sebuf.http.query)` are parsed from URL query string instead of request body. This is useful for GET requests that shouldn't have a body.
223+
224+
## Client Examples
225+
226+
This directory contains several example files demonstrating advanced client usage patterns:
227+
228+
| File | Description |
229+
|------|-------------|
230+
| `client_example.go` | Basic CRUD operations with the generated client |
231+
| `client_error_handling.go` | Comprehensive error handling patterns |
232+
| `client_content_types.go` | JSON vs binary protobuf content types |
233+
| `client_per_call_options.go` | Per-request customization with call options |
234+
235+
### Running the Examples
236+
237+
First, start the server:
238+
```bash
239+
make run
240+
```
241+
242+
Then run any example:
243+
```bash
244+
go run client_example.go
245+
go run client_error_handling.go
246+
go run client_content_types.go
247+
go run client_per_call_options.go
248+
```
249+
250+
### Error Handling Example
251+
252+
Demonstrates handling different error types:
253+
- Validation errors (HTTP 400) with field-level details
254+
- Not found errors (HTTP 404)
255+
- Missing required headers
256+
- Network errors and timeouts
257+
- Context cancellation
258+
259+
```go
260+
var validationErr *sebufhttp.ValidationError
261+
if errors.As(err, &validationErr) {
262+
for _, violation := range validationErr.GetViolations() {
263+
fmt.Printf("Field '%s': %s\n", violation.GetField(), violation.GetDescription())
264+
}
265+
}
266+
```
267+
268+
### Content Types Example
269+
270+
Demonstrates switching between JSON and binary protobuf:
271+
- Default JSON client
272+
- Binary protobuf for better performance
273+
- Per-request content type override
274+
- Performance comparison
275+
276+
```go
277+
// Default JSON
278+
client := services.NewProductServiceClient("http://localhost:8080")
279+
280+
// Binary protobuf (30-50% smaller payloads)
281+
protoClient := services.NewProductServiceClient(
282+
"http://localhost:8080",
283+
services.WithProductServiceContentType(services.ContentTypeProto),
284+
)
285+
286+
// Per-request override
287+
client.ListProducts(ctx, req,
288+
services.WithProductServiceCallContentType(services.ContentTypeProto),
289+
)
290+
```
291+
292+
### Per-Call Options Example
293+
294+
Demonstrates request-level customization:
295+
- Custom headers per request (tracing, correlation IDs)
296+
- Generated header helpers
297+
- Content type override per request
298+
- Dynamic headers based on context
299+
- A/B testing with headers
300+
301+
```go
302+
product, err := client.CreateProduct(ctx, req,
303+
services.WithProductServiceHeader("X-Request-ID", "req-123"),
304+
services.WithProductServiceHeader("X-Correlation-ID", "corr-456"),
305+
services.WithProductServiceCallContentType(services.ContentTypeProto),
306+
)
307+
```
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
//go:build ignore
2+
3+
// This example demonstrates content type switching between JSON and binary protobuf.
4+
// Run with: go run client_content_types.go
5+
package main
6+
7+
import (
8+
"context"
9+
"fmt"
10+
"log"
11+
"net/http"
12+
"time"
13+
14+
"github.com/SebastienMelki/sebuf/examples/restful-crud/api/proto/models"
15+
"github.com/SebastienMelki/sebuf/examples/restful-crud/api/proto/services"
16+
)
17+
18+
func main() {
19+
ctx := context.Background()
20+
21+
fmt.Println("=== Content Type Examples ===\n")
22+
23+
// Example 1: Default JSON client
24+
fmt.Println("1. JSON Client (Default)")
25+
jsonClient := services.NewProductServiceClient(
26+
"http://localhost:8080",
27+
services.WithProductServiceAPIKey("123e4567-e89b-12d3-a456-426614174000"),
28+
// JSON is the default, no need to specify
29+
)
30+
31+
product, err := createTestProduct(ctx, jsonClient, "JSON Product")
32+
if err != nil {
33+
log.Printf(" Error: %v\n", err)
34+
} else {
35+
fmt.Printf(" Created product via JSON: %s (ID: %s)\n", product.Name, product.Id)
36+
}
37+
38+
// Example 2: Binary protobuf client (better performance for large payloads)
39+
fmt.Println("\n2. Binary Protobuf Client (Better Performance)")
40+
protoClient := services.NewProductServiceClient(
41+
"http://localhost:8080",
42+
services.WithProductServiceAPIKey("123e4567-e89b-12d3-a456-426614174000"),
43+
services.WithProductServiceContentType(services.ContentTypeProto),
44+
)
45+
46+
product, err = createTestProduct(ctx, protoClient, "Protobuf Product")
47+
if err != nil {
48+
log.Printf(" Error: %v\n", err)
49+
} else {
50+
fmt.Printf(" Created product via Protobuf: %s (ID: %s)\n", product.Name, product.Id)
51+
}
52+
53+
// Example 3: Per-request content type override
54+
fmt.Println("\n3. Per-Request Content Type Override")
55+
fmt.Println(" Using JSON client but overriding to Protobuf for one request...")
56+
57+
// Client defaults to JSON
58+
mixedClient := services.NewProductServiceClient(
59+
"http://localhost:8080",
60+
services.WithProductServiceAPIKey("123e4567-e89b-12d3-a456-426614174000"),
61+
// Default: JSON
62+
)
63+
64+
// But this specific request uses Protobuf
65+
product, err = mixedClient.CreateProduct(ctx, &models.CreateProductRequest{
66+
Name: "Mixed Content Product",
67+
Description: "Created with Protobuf override",
68+
Price: 39.99,
69+
CategoryId: "mixed",
70+
},
71+
services.WithProductServiceCallContentType(services.ContentTypeProto),
72+
)
73+
74+
if err != nil {
75+
log.Printf(" Error: %v\n", err)
76+
} else {
77+
fmt.Printf(" Created product with Protobuf override: %s\n", product.Name)
78+
}
79+
80+
// Example 4: Performance comparison
81+
fmt.Println("\n4. Performance Comparison (JSON vs Protobuf)")
82+
runPerformanceComparison(ctx, jsonClient, protoClient)
83+
84+
// Example 5: When to use each content type
85+
fmt.Println("\n5. Content Type Guidelines")
86+
fmt.Println(" JSON (application/json):")
87+
fmt.Println(" - Human-readable, easy to debug")
88+
fmt.Println(" - Better for browser/JavaScript clients")
89+
fmt.Println(" - Larger payload size")
90+
fmt.Println(" - Default choice for most APIs")
91+
fmt.Println("")
92+
fmt.Println(" Protobuf (application/x-protobuf):")
93+
fmt.Println(" - Smaller payload size (30-50% smaller)")
94+
fmt.Println(" - Faster serialization/deserialization")
95+
fmt.Println(" - Better for high-throughput services")
96+
fmt.Println(" - Better for large payloads or batch operations")
97+
fmt.Println(" - Requires protobuf-aware clients")
98+
99+
fmt.Println("\n=== Content Type Examples Complete ===")
100+
}
101+
102+
func createTestProduct(ctx context.Context, client services.ProductServiceClient, name string) (*models.Product, error) {
103+
return client.CreateProduct(ctx, &models.CreateProductRequest{
104+
Name: name,
105+
Description: "Test product for content type demo",
106+
Price: 29.99,
107+
CategoryId: "test",
108+
Tags: []string{"demo", "test"},
109+
})
110+
}
111+
112+
func runPerformanceComparison(ctx context.Context, jsonClient, protoClient services.ProductServiceClient) {
113+
iterations := 10
114+
115+
// Measure JSON performance
116+
jsonStart := time.Now()
117+
for i := 0; i < iterations; i++ {
118+
_, err := jsonClient.ListProducts(ctx, &models.ListProductsRequest{
119+
Page: 1,
120+
Limit: 50,
121+
})
122+
if err != nil {
123+
log.Printf(" JSON request %d failed: %v", i, err)
124+
return
125+
}
126+
}
127+
jsonDuration := time.Since(jsonStart)
128+
129+
// Measure Protobuf performance
130+
protoStart := time.Now()
131+
for i := 0; i < iterations; i++ {
132+
_, err := protoClient.ListProducts(ctx, &models.ListProductsRequest{
133+
Page: 1,
134+
Limit: 50,
135+
})
136+
if err != nil {
137+
log.Printf(" Protobuf request %d failed: %v", i, err)
138+
return
139+
}
140+
}
141+
protoDuration := time.Since(protoStart)
142+
143+
fmt.Printf(" JSON: %d requests in %v (avg: %v/req)\n",
144+
iterations, jsonDuration, jsonDuration/time.Duration(iterations))
145+
fmt.Printf(" Protobuf: %d requests in %v (avg: %v/req)\n",
146+
iterations, protoDuration, protoDuration/time.Duration(iterations))
147+
148+
if protoDuration < jsonDuration {
149+
improvement := float64(jsonDuration-protoDuration) / float64(jsonDuration) * 100
150+
fmt.Printf(" Protobuf is %.1f%% faster\n", improvement)
151+
} else {
152+
fmt.Println(" (Results may vary based on payload size and network conditions)")
153+
}
154+
}

0 commit comments

Comments
 (0)