8.5 KiB
8.5 KiB
Quick Start: Notifier Client Integration
5-minute guide to integrating with the Notifier service
Essential Requirements
1. Connection: Use Long-Lived gRPC Connection ⚡
// ✅ DO THIS: Create once, reuse forever
client, _ := NewNotifierClient("notifier-grpc:50051", apiKey)
defer client.Close()
for i := 0; i < 10000; i++ {
client.SendNotification(ctx, req) // Reuse connection
}
// ❌ DON'T DO THIS: Creating connection per request
for i := 0; i < 10000; i++ {
client, _ := NewNotifierClient("notifier-grpc:50051", apiKey)
client.SendNotification(ctx, req)
client.Close() // SLOW: 10x slower!
}
Why? Connection reuse is 10x faster (1.5ms vs 15ms per request)
2. Authentication: Send API Key with Every Request 🔐
import "google.golang.org/grpc/metadata"
// Add API key to metadata for each request
ctx = metadata.AppendToOutgoingContext(ctx,
"authorization", "Bearer "+apiKey)
resp, err := client.SendNotification(ctx, req)
Two accepted formats:
authorization: Bearer nk_your_api_key(preferred)x-api-key: nk_your_api_key
API Key Format: nk_ + 64 hex characters (e.g., nk_a1b2c3d4...)
3. Credentials: Load from Environment 🔒
// ✅ DO THIS
apiKey := os.Getenv("NOTIFIER_API_KEY")
// ❌ DON'T DO THIS
apiKey := "nk_abc123..." // NEVER hardcode!
Kubernetes Secret:
apiVersion: v1
kind: Secret
metadata:
name: notifier-credentials
stringData:
api-key: nk_your_actual_key_here
---
# In your deployment:
env:
- name: NOTIFIER_API_KEY
valueFrom:
secretKeyRef:
name: notifier-credentials
key: api-key
4. Keepalive: Prevent Connection Timeouts ⏰
import "google.golang.org/grpc/keepalive"
conn, err := grpc.Dial(address,
grpc.WithKeepaliveParams(keepalive.ClientParameters{
Time: 10 * time.Second, // Ping every 10s
Timeout: 3 * time.Second, // Wait 3s for response
PermitWithoutStream: true, // Ping when idle
}),
)
5. Error Handling: Retry Only Specific Errors 🔄
err := client.SendNotification(ctx, req)
switch status.Code(err) {
case codes.ResourceExhausted:
// Rate limit exceeded - RETRY with backoff
time.Sleep(100 * time.Millisecond)
// Try again...
case codes.Unauthenticated:
// Invalid API key - DON'T RETRY, log error
log.Errorf("Authentication failed: %v", err)
case codes.InvalidArgument:
// Bad request - DON'T RETRY, fix request
log.Errorf("Invalid request: %v", err)
case codes.Unavailable:
// Service down - RETRY with backoff
time.Sleep(100 * time.Millisecond)
// Try again...
}
Retry Strategy:
- Exponential backoff: 100ms → 200ms → 400ms → 800ms
- Max 3-5 attempts
- Only retry:
ResourceExhausted,Unavailable,DeadlineExceeded
6. Context Timeout: Always Set Timeout ⏱️
// ✅ DO THIS: Set timeout for each request
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
resp, err := client.SendNotification(ctx, req)
// ❌ DON'T DO THIS: No timeout
ctx := context.Background()
resp, err := client.SendNotification(ctx, req) // Could hang forever!
Complete Minimal Example
package main
import (
"context"
"log"
"os"
"time"
pb "github.com/igodwin/notifier/api/grpc/pb"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
"google.golang.org/grpc/keepalive"
"google.golang.org/grpc/metadata"
)
type NotifierClient struct {
conn *grpc.ClientConn
client pb.NotifierServiceClient
apiKey string
}
func NewNotifierClient(address, apiKey string) (*NotifierClient, error) {
// Create long-lived connection
conn, err := grpc.Dial(address,
grpc.WithTransportCredentials(insecure.NewCredentials()),
grpc.WithKeepaliveParams(keepalive.ClientParameters{
Time: 10 * time.Second,
Timeout: 3 * time.Second,
PermitWithoutStream: true,
}),
)
if err != nil {
return nil, err
}
return &NotifierClient{
conn: conn,
client: pb.NewNotifierServiceClient(conn),
apiKey: apiKey,
}, nil
}
func (nc *NotifierClient) SendNotification(ctx context.Context, req *pb.SendNotificationRequest) (*pb.SendNotificationResponse, error) {
// Add timeout
ctx, cancel := context.WithTimeout(ctx, 10*time.Second)
defer cancel()
// Add API key to metadata
ctx = metadata.AppendToOutgoingContext(ctx, "authorization", "Bearer "+nc.apiKey)
// Make RPC call
return nc.client.SendNotification(ctx, req)
}
func (nc *NotifierClient) Close() error {
return nc.conn.Close()
}
func main() {
// Load credentials
address := os.Getenv("NOTIFIER_ADDRESS")
apiKey := os.Getenv("NOTIFIER_API_KEY")
if address == "" {
address = "notifier-grpc:50051"
}
if apiKey == "" {
log.Fatal("NOTIFIER_API_KEY environment variable required")
}
// Create client (once)
client, err := NewNotifierClient(address, apiKey)
if err != nil {
log.Fatalf("Failed to create client: %v", err)
}
defer client.Close()
// Send notification
resp, err := client.SendNotification(context.Background(), &pb.SendNotificationRequest{
Type: pb.NotificationType_NOTIFICATION_TYPE_EMAIL,
Subject: "Test Notification",
Body: "This is a test from the notifier client",
Recipients: []string{"user@example.com"},
Priority: pb.Priority_PRIORITY_NORMAL,
})
if err != nil {
log.Fatalf("Failed to send notification: %v", err)
}
log.Printf("Notification sent successfully: %s", resp.NotificationId)
}
Service Connection Details
Address:
- Same namespace:
notifier-grpc:50051 - Different namespace:
notifier-grpc.default.svc.cluster.local:50051
Rate Limit: 100 requests/minute (default, configurable per key)
Available Methods:
SendNotification- Send single notificationSendBatchNotifications- Send multiple notificationsGetNotification- Get notification statusListNotifications- List recent notificationsCancelNotification- Cancel pending notificationRetryNotification- Retry failed notificationGetStats- Get service statisticsGetNotifiers- List available notifiersHealthCheck- Check service health
Notification Types:
NOTIFICATION_TYPE_EMAIL- Email via SMTPNOTIFICATION_TYPE_SLACK- Slack webhookNOTIFICATION_TYPE_NTFY- Ntfy.sh push notificationsNOTIFICATION_TYPE_STDOUT- Console output (dev only)
Security Checklist
- API key loaded from environment variable
- No hardcoded credentials in source code
- API key never logged in full (mask it:
nk_abc...xyz) - Kubernetes Secret created for API key
- Deployment configured to load secret as env var
.gitignoreexcludes any credential files
Testing Your Integration
# Set environment variables
export NOTIFIER_ADDRESS="notifier-grpc:50051"
export NOTIFIER_API_KEY="nk_your_api_key_here"
# Run your application
go run main.go
# Check logs for:
# ✅ "Notification sent successfully: <id>"
# ❌ "Authentication failed" - check API key
# ❌ "Rate limit exceeded" - slow down requests
Common Issues
"Unauthenticated" error:
- Check API key format starts with
nk_ - Verify key is 66 characters total (nk_ + 64 hex)
- Confirm key is active in notifier service
- Check you're adding it to metadata correctly
"Rate limit exceeded" error:
- Implement exponential backoff retry
- Consider batching notifications
- Request higher rate limit for your key
Connection timeouts:
- Add keepalive configuration
- Check network connectivity to
notifier-grpc:50051 - Verify notifier service is running:
kubectl get pods -l app=notifier
Slow performance:
- Confirm you're reusing connection (not creating per request)
- Check keepalive is configured
- Verify you're using connection pooling for high concurrency
Next Steps
For more details, see:
CLIENT_INTEGRATION_PROMPT.md- Complete prompt for AI assistantsGRPC_CONNECTION_OPTIMIZATION.md- Deep dive on performanceCLIENT_RECOMMENDATIONS.md- Advanced patterns and best practicesAUTH.md- Authentication system details
Getting Your API Key
Contact your Notifier service administrator to:
- Create an API key for your application
- Set appropriate rate limits
- Assign required roles for notifier access
- Get the key in format:
nk_<64-hex-characters>
Store it securely and never commit it to version control!