### Initialize a JSON-RPC Server Source: https://context7.com/semrush/zenrpc/llms.txt Configures and starts a new JSON-RPC 2.0 server instance with options for batch limits, CORS, and SMD exposure. ```go package main import ( "log" "net/http" "os" "github.com/semrush/zenrpc/v2" ) func main() { // Create server with options rpc := zenrpc.NewServer(zenrpc.Options{ BatchMaxLen: 10, // Max requests in a batch (default: 10) TargetURL: "/", // RPC endpoint URL ExposeSMD: true, // Enable ?smd query parameter AllowCORS: true, // Add Access-Control-Allow-Origin: * DisableTransportChecks: false, // Enforce Content-Type and POST method HideErrorDataField: false, // Show error data in responses }) // Start HTTP server http.Handle("/", rpc) log.Fatal(http.ListenAndServe(":9999", nil)) } ``` -------------------------------- ### Define RPC Service and Server Source: https://github.com/semrush/zenrpc/blob/master/README.md Example of defining an ArithService struct with RPC methods and initializing the zenrpc server with middleware and registration. ```go package main import ( "flag" "context" "errors" "math" "log" "net/http" "os" "github.com/semrush/zenrpc/v2" "github.com/semrush/zenrpc/v2/testdata" ) type ArithService struct{ zenrpc.Service } // Sum sums two digits and returns error with error code as result and IP from context. func (as ArithService) Sum(ctx context.Context, a, b int) (bool, *zenrpc.Error) { r, _ := zenrpc.RequestFromContext(ctx) return true, zenrpc.NewStringError(a+b, r.Host) } // Multiply multiples two digits and returns result. func (as ArithService) Multiply(a, b int) int { return a * b } type Quotient struct { Quo, Rem int } func (as ArithService) Divide(a, b int) (quo *Quotient, err error) { if b == 0 { return nil, errors.New("divide by zero") } else if b == 1 { return nil, zenrpc.NewError(401, errors.New("we do not serve 1")) } return &Quotient{ Quo: a / b, Rem: a % b, }, nil } // Pow returns x**y, the base-x exponential of y. If Exp is not set then default value is 2. //zenrpc:exp=2 func (as ArithService) Pow(base float64, exp float64) float64 { return math.Pow(base, exp) } //go:generate zenrpc func main() { addr := flag.String("addr", "localhost:9999", "listen address") flag.Parse() rpc := zenrpc.NewServer(zenrpc.Options{ExposeSMD: true}) rpc.Register("arith", testdata.ArithService{}) rpc.Register("", testdata.ArithService{}) // public rpc.Use(zenrpc.Logger(log.New(os.Stderr, "", log.LstdFlags))) http.Handle("/", rpc) log.Printf("starting arithsrv on %s", *addr) log.Fatal(http.ListenAndServe(*addr, nil)) } ``` -------------------------------- ### Configuring SMD Schema and API Documentation Source: https://context7.com/semrush/zenrpc/llms.txt Enable SMD generation in server options to expose API documentation via query parameters or the built-in SMDBox handler. ```go package main import ( "encoding/json" "fmt" "log" "net/http" "github.com/semrush/zenrpc/v2" "github.com/semrush/zenrpc/v2/testdata" ) func main() { rpc := zenrpc.NewServer(zenrpc.Options{ ExposeSMD: true, TargetURL: "/rpc", }) rpc.Register("arith", testdata.ArithService{}) // Programmatically access SMD schema schema := rpc.SMD() schemaJSON, _ := json.MarshalIndent(schema, "", " ") fmt.Println(string(schemaJSON)) // Output: {"transport":"POST","envelope":"JSON-RPC-2.0","contentType":"application/json",...} // RPC endpoint http.Handle("/rpc", rpc) // SMD schema available at: GET /rpc?smd // SMDBox web UI for API browsing http.HandleFunc("/doc", zenrpc.SMDBoxHandler) log.Fatal(http.ListenAndServe(":9999", nil)) } ``` -------------------------------- ### Register Services with Namespaces Source: https://context7.com/semrush/zenrpc/llms.txt Binds services to specific namespaces using the Register method or registers multiple services simultaneously via RegisterAll. ```go package main import ( "log" "net/http" "github.com/semrush/zenrpc/v2" "github.com/semrush/zenrpc/v2/testdata" ) func main() { rpc := zenrpc.NewServer(zenrpc.Options{ExposeSMD: true}) // Register services with namespaces rpc.Register("arith", testdata.ArithService{}) // Called as "arith.multiply" rpc.Register("phonebook", testdata.PhoneBook{}) // Called as "phonebook.lookup" rpc.Register("", testdata.ArithService{}) // Public namespace, called as "multiply" // Or register multiple at once rpc.RegisterAll(map[string]zenrpc.Invoker{ "calc": testdata.ArithService{}, "phone": testdata.PhoneBook{}, }) http.Handle("/", rpc) log.Fatal(http.ListenAndServe(":9999", nil)) } ``` -------------------------------- ### Configuring WebSocket Transport with ZenRPC Source: https://context7.com/semrush/zenrpc/llms.txt Set up a WebSocket endpoint using `ServeWS` for bidirectional communication. Configure the `websocket.Upgrader` in server options to customize WebSocket behavior. ```go package main import ( "log" "net/http" "github.com/gorilla/websocket" "github.com/semrush/zenrpc/v2" "github.com/semrush/zenrpc/v2/testdata" ) func main() { rpc := zenrpc.NewServer(zenrpc.Options{ ExposeSMD: true, AllowCORS: true, Upgrader: &websocket.Upgrader{ CheckOrigin: func(r *http.Request) bool { return true }, ReadBufferSize: 1024, WriteBufferSize: 1024, }, }) rpc.Register("arith", testdata.ArithService{}) // HTTP endpoint http.Handle("/", rpc) // WebSocket endpoint http.HandleFunc("/ws", rpc.ServeWS) log.Fatal(http.ListenAndServe(":9999", nil)) } ``` -------------------------------- ### Define RPC Services Source: https://context7.com/semrush/zenrpc/llms.txt Defines RPC endpoints using Go structs. Embed zenrpc.Service or use the //zenrpc comment to enable code generation for methods. ```go package myservice import ( "context" "errors" "math" "github.com/semrush/zenrpc/v2" ) // ArithService provides arithmetic operations type ArithService struct{ zenrpc.Service } // Multiply multiplies two numbers and returns the result func (as ArithService) Multiply(a, b int) int { return a * b } // Divide divides two numbers with error handling //zenrpc:a the dividend //zenrpc:b the divisor //zenrpc:401 division by one not allowed //zenrpc:-32603 divide by zero error func (as *ArithService) Divide(a, b int) (quo *Quotient, err error) { if b == 0 { return nil, errors.New("divide by zero") } if b == 1 { return nil, zenrpc.NewError(401, errors.New("we do not serve 1")) } return &Quotient{Quo: a / b, Rem: a % b}, nil } // Pow calculates base raised to exp power. Default exp is 2. //zenrpc:exp=2 exponent (optional, defaults to 2) func (as *ArithService) Pow(base float64, exp *float64) float64 { return math.Pow(base, *exp) } // Sum demonstrates context usage and custom error responses func (as ArithService) Sum(ctx context.Context, a, b int) (bool, *zenrpc.Error) { r, _ := zenrpc.RequestFromContext(ctx) return true, zenrpc.NewStringError(a+b, r.Host) } type Quotient struct { Quo int `json:"quo"` Rem int `json:"rem"` } //go:generate zenrpc ``` -------------------------------- ### Registering Middleware in ZenRPC Server Source: https://context7.com/semrush/zenrpc/llms.txt Register built-in and custom middleware functions to wrap method invocations. Middleware is applied in the order it is registered. ```go package main import ( "context" "encoding/json" "log" "net/http" "os" "time" "github.com/semrush/zenrpc/v2" ) // Custom authentication middleware func AuthMiddleware(h zenrpc.InvokeFunc) zenrpc.InvokeFunc { return func(ctx context.Context, method string, params json.RawMessage) zenrpc.Response { req, ok := zenrpc.RequestFromContext(ctx) if !ok || req.Header.Get("Authorization") == "" { return zenrpc.NewResponseError(nil, zenrpc.InvalidRequest, "unauthorized", nil) } return h(ctx, method, params) } } // Custom timing middleware func TimingMiddleware(h zenrpc.InvokeFunc) zenrpc.InvokeFunc { return func(ctx context.Context, method string, params json.RawMessage) zenrpc.Response { start := time.Now() resp := h(ctx, method, params) log.Printf("Method %s took %v", method, time.Since(start)) return resp } } func main() { rpc := zenrpc.NewServer(zenrpc.Options{ExposeSMD: true}) // Register built-in middleware rpc.Use(zenrpc.Logger(log.New(os.Stderr, "[RPC] ", log.LstdFlags))) rpc.Use(zenrpc.Metrics("myapp")) // Prometheus metrics // Register custom middleware rpc.Use(AuthMiddleware) rpc.Use(TimingMiddleware) http.Handle("/", rpc) log.Fatal(http.ListenAndServe(":9999", nil)) } ``` -------------------------------- ### Accessing Request Context in Go Source: https://context7.com/semrush/zenrpc/llms.txt Use context functions to retrieve HTTP request details, namespaces, and request IDs within service methods. Ensure the service method accepts a context.Context parameter. ```go package myservice import ( "context" "fmt" "github.com/semrush/zenrpc/v2" ) type UserService struct{ zenrpc.Service } // GetUserInfo demonstrates context access func (us UserService) GetUserInfo(ctx context.Context, userID int) (*UserInfo, error) { // Get the HTTP request from context req, ok := zenrpc.RequestFromContext(ctx) if !ok { return nil, zenrpc.NewStringError(-32000, "no request in context") } // Access request details clientIP := req.RemoteAddr authHeader := req.Header.Get("Authorization") userAgent := req.UserAgent() // Get the current namespace (e.g., "user" if called as "user.getuserinfo") namespace := zenrpc.NamespaceFromContext(ctx) // Get the request ID for logging/tracing requestID := zenrpc.IDFromContext(ctx) fmt.Printf("Request from %s, namespace: %s, id: %v\n", clientIP, namespace, requestID) return &UserInfo{ ID: userID, IP: clientIP, UserAgent: userAgent, HasAuth: authHeader != "", }, nil } type UserInfo struct { ID int `json:"id"` IP string `json:"ip"` UserAgent string `json:"user_agent"` HasAuth bool `json:"has_auth"` } //go:generate zenrpc ``` -------------------------------- ### Process JSON-RPC Requests Directly Source: https://context7.com/semrush/zenrpc/llms.txt Use the `Do` method to process JSON-RPC requests directly without HTTP. This is useful for testing or embedding ZenRPC in other protocols. Ensure the `context` and request payload are correctly formatted. ```go package main import ( "context" "fmt" "github.com/semrush/zenrpc/v2" "github.com/semrush/zenrpc/v2/testdata" ) func main() { rpc := zenrpc.NewServer(zenrpc.Options{}) rpc.Register("arith", testdata.ArithService{}) // Process request directly request := []byte(`{"jsonrpc":"2.0","id":1,"method":"arith.multiply","params":[6,7]}`) response, err := rpc.Do(context.Background(), request) if err != nil { panic(err) } fmt.Println(string(response)) // Output: {"jsonrpc":"2.0","id":1,"result":42} // Process batch request batchRequest := []byte(`[ {"jsonrpc":"2.0","id":1,"method":"arith.multiply","params":[2,3]}, {"jsonrpc":"2.0","id":2,"method":"arith.pi"} ]`) batchResponse, _ := rpc.Do(context.Background(), batchRequest) fmt.Println(string(batchResponse)) // Output: [{"jsonrpc":"2.0","id":1,"result":6},{"jsonrpc":"2.0","id":2,"result":3.141592653589793}] } ``` -------------------------------- ### Add Prometheus Metrics Middleware Source: https://context7.com/semrush/zenrpc/llms.txt Integrate the `Metrics` middleware to export RPC error counts and response duration metrics to Prometheus. This middleware registers metrics with a specified application name prefix. Ensure Prometheus client libraries are imported. ```go package main import ( "log" "net/http" "github.com/prometheus/client_golang/prometheus/promhttp" "github.com/semrush/zenrpc/v2" "github.com/semrush/zenrpc/v2/testdata" ) func main() { rpc := zenrpc.NewServer(zenrpc.Options{ExposeSMD: true}) rpc.Register("arith", testdata.ArithService{}) // Add Prometheus metrics middleware // Registers: myapp_rpc_error_requests_count{method,code} // Registers: myapp_rpc_responses_duration_seconds{method,code} rpc.Use(zenrpc.Metrics("myapp")) // RPC endpoint http.Handle("/", rpc) // Prometheus metrics endpoint http.Handle("/metrics", promhttp.Handler()) log.Fatal(http.ListenAndServe(":9999", nil)) } // Prometheus metrics output example: // myapp_rpc_error_requests_count{method="arith.divide",code="401"} 5 // myapp_rpc_responses_duration_seconds{method="arith.multiply",code=""} 0.001234 ``` -------------------------------- ### Making JSON-RPC Requests via cURL Source: https://context7.com/semrush/zenrpc/llms.txt Execute various JSON-RPC operations including positional/named parameters, batch requests, and notifications using standard HTTP POST requests. ```bash # Single request with positional parameters curl -X POST http://localhost:9999/ \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","id":1,"method":"arith.multiply","params":[5,3]}' # Response: {"jsonrpc":"2.0","id":1,"result":15} # Single request with named parameters curl -X POST http://localhost:9999/ \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","id":2,"method":"arith.divide","params":{"a":10,"b":3}}' # Response: {"jsonrpc":"2.0","id":2,"result":{"quo":3,"rem":1}} # Request with default parameter (exp defaults to 2) curl -X POST http://localhost:9999/ \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","id":3,"method":"arith.pow","params":{"base":4}}' # Response: {"jsonrpc":"2.0","id":3,"result":16} # Public namespace (no prefix) curl -X POST http://localhost:9999/ \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","id":4,"method":"multiply","params":[2,7]}' # Response: {"jsonrpc":"2.0","id":4,"result":14} # Batch request curl -X POST http://localhost:9999/ \ -H "Content-Type: application/json" \ -d '[ {"jsonrpc":"2.0","id":1,"method":"arith.multiply","params":[2,3]}, {"jsonrpc":"2.0","id":2,"method":"arith.pi"}, {"jsonrpc":"2.0","method":"arith.dosomething"} ]' # Response: [{"jsonrpc":"2.0","id":1,"result":6},{"jsonrpc":"2.0","id":2,"result":3.141592653589793}] # Note: Notification (no id) returns no response # Notification (no response expected) curl -X POST http://localhost:9999/ \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","method":"arith.dosomething"}' # Response: (empty) # Get SMD schema curl "http://localhost:9999/?smd" # Response: {"transport":"POST","envelope":"JSON-RPC-2.0",...} ``` -------------------------------- ### Handling Custom Errors in ZenRPC Source: https://context7.com/semrush/zenrpc/llms.txt Implement custom error responses using `zenrpc.NewError` or `zenrpc.NewStringError` to provide specific error codes and messages. Standard JSON-RPC 2.0 error codes are also supported. ```go package myservice import ( "errors" "github.com/semrush/zenrpc/v2" ) type PaymentService struct{ zenrpc.Service } // ProcessPayment handles payment with various error conditions //zenrpc:400 invalid amount //zenrpc:401 unauthorized //zenrpc:500 payment gateway error func (ps PaymentService) ProcessPayment(amount float64, cardToken string) (*Receipt, error) { if amount <= 0 { // Return standard error (becomes InternalError -32603) return nil, errors.New("amount must be positive") } if cardToken == "" { // Return error with custom code return nil, zenrpc.NewError(401, errors.New("card token required")) } if amount > 10000 { // Return error with code and message return nil, zenrpc.NewStringError(400, "amount exceeds limit") } // Create response with error data field if gatewayErr := processGateway(amount, cardToken); gatewayErr != nil { err := &zenrpc.Error{ Code: 500, Message: "gateway error", Data: map[string]string{"gateway_code": "GW_TIMEOUT"}, } return nil, err } return &Receipt{ID: "12345", Amount: amount}, nil } // Standard JSON-RPC 2.0 error codes: // zenrpc.ParseError = -32700 // Invalid JSON // zenrpc.InvalidRequest = -32600 // Invalid Request object // zenrpc.MethodNotFound = -32601 // Method not found // zenrpc.InvalidParams = -32602 // Invalid parameters // zenrpc.InternalError = -32603 // Internal error // zenrpc.ServerError = -32000 // Server error type Receipt struct { ID string `json:"id"` Amount float64 `json:"amount"` } func processGateway(amount float64, token string) error { return nil } //go:generate zenrpc ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.