autorpc

package module
v1.0.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 8, 2025 License: MIT Imports: 13 Imported by: 0

README

autorpc

A type-safe, JSON-RPC 2.0 server library for Go. With minimal boilerplate, automatic request handling, validation, middlewares, and introspection.

Features

  • Type-Safe: Uses Go generics for compile-time type checking
  • Automatic Validation: Built-in parameter validation using struct tags
  • Introspection UI (wip): Built-in web UI for exploring your API

Installation

go get github.com/Lexographics/autorpc

Quick Start

package main

import (
	"context"
	"log"
	"net/http"

	"github.com/Lexographics/autorpc"
)

func Greet(ctx context.Context, name string) (string, error) {
	return "Hello, " + name + "!", nil
}

func main() {
	server := autorpc.NewServer()
	
	// Register a method
	autorpc.RegisterMethod(server, "greet", Greet)
	
	// Set up HTTP handler
	http.Handle("/rpc", autorpc.HTTPHandler(server))
	
	log.Println("Server started on port 8080")
	http.ListenAndServe(":8080", nil)
}

Call your RPC method:

curl -X POST http://localhost:8080/rpc \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","method":"greet","params":"John","id":1}'

Response:

{
  "jsonrpc": "2.0",
  "result": "Hello, John!",
  "id": 1
}

Examples

Method with Struct Parameters
type AddParams struct {
	A float32 `json:"a" validate:"required"`
	B float32 `json:"b" validate:"required"`
}

func Add(ctx context.Context, params AddParams) (float32, error) {
	return params.A + params.B, nil
}

autorpc.RegisterMethod(server, "math.add", Add)
Using Groups and Middleware
// Global middleware
server.Use(ExampleMiddleware(""))

// Create group with prefix and middleware
mathGroup := server.Group("math.", ExampleMiddleware("math"))

// Register methods in group
autorpc.RegisterMethod(mathGroup, "add", Add)
autorpc.RegisterMethod(mathGroup, "multiply", Multiply)

// Register with method-specific middleware
autorpc.RegisterMethod(mathGroup, "divide", Divide, ExampleMiddleware("divide"))
Middleware
func ExampleMiddleware(text string) autorpc.Middleware {
	return func(ctx context.Context, req autorpc.RPCRequest, next autorpc.HandlerFunc) (autorpc.RPCResponse, error) {
		log.Println(text)
		return next(ctx, req)
	}
}
HTTP Context Access
func MyMiddleware(ctx context.Context, req autorpc.RPCRequest, next autorpc.HandlerFunc) (autorpc.RPCResponse, error) {
	httpReq := autorpc.HTTPRequestFromContext(ctx)
	if httpReq != nil {
		userAgent := httpReq.Header.Get("User-Agent")
		cookie, _ := httpReq.Cookie("session_id")
	}
	return next(ctx, req)
}

API Reference

Server
server := autorpc.NewServer()
server.Use(middlewares ...Middleware) // Add global middleware
RegisterMethod
autorpc.RegisterMethod[P, R any](r Registerer, name string, fn func(context.Context, P) (R, error), middlewares ...Middleware)
  • r: Can be *Server or *Group
  • name: Method name (prefix added automatically for groups)
  • fn: Handler function
  • middlewares: Optional method-specific middleware
Groups
group := server.Group(prefix string, middlewares ...Middleware)
group.Use(middlewares ...Middleware) // Add middleware to group
Validation

Use struct tags from github.com/go-playground/validator/v10:

type Params struct {
	Name string `json:"name" validate:"required,min=3"`
	Age  int    `json:"age" validate:"required,min=18"`
}
Custom Errors

Implement RPCErrorProvider:

type CustomError struct {
	code    int
	message string
	data interface{}
}

func (e *CustomError) Error() string { return e.message }
func (e *CustomError) Code() int     { return e.code }
func (e *CustomError) Message() string { return e.message }
func (e *CustomError) Data() interface{} { return e.data }

Method Signature

All methods must follow this signature:

func MethodName(ctx context.Context, params ParamsType) (ResultType, error)
  • First parameter: context.Context
  • Second parameter: Any type (primitive, struct, slice, pointer, etc.)
  • Returns: (ResultType, error)

Middleware Execution Order

  1. Global middleware (server.Use(...))
  2. Group middleware (group.Use(...))
  3. Method-specific middleware (RegisterMethod(..., middleware...))
  4. Handler

Note: Middleware is captured at registration time. Methods registered before middleware is added won't have that middleware.

License

MIT

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Documentation

Index

Constants

View Source
const (
	CodeParseError     = -32700
	CodeInvalidRequest = -32600
	CodeMethodNotFound = -32601
	CodeInvalidParams  = -32602
	CodeInternalError  = -32603
)

Variables

This section is empty.

Functions

func HTTPHandler

func HTTPHandler(server *Server) http.Handler

func HTTPRequestFromContext added in v1.0.1

func HTTPRequestFromContext(ctx context.Context) *http.Request

func RegisterMethod

func RegisterMethod[P, R any](
	r Registerer,
	name string,
	fn func(context.Context, P) (R, error),
	middlewares ...Middleware,
)

RegisterMethod registers a method with the given name and function. The first parameter can be either *Server or *Group. The function must have the signature: func(context.Context, ParamsType) (ResultType, error)

Example with Server:

RegisterMethod(server, "add", func(ctx context.Context, params []int) (int, error) {
    if len(params) != 2 {
        return 0, errors.New("expected 2 numbers")
    }
    return params[0] + params[1], nil
})

Example with Group:

mathGroup := server.Group("math.")
RegisterMethod(mathGroup, "add", AddFunc)

With middleware:

RegisterMethod(server, "add", AddFunc, AuthMiddleware(), LoggingMiddleware())

func SpecJSONHandler

func SpecJSONHandler(server *Server) http.Handler

func SpecUIHandler added in v1.0.2

func SpecUIHandler(server *Server) http.Handler

func WithHTTPRequest added in v1.0.1

func WithHTTPRequest(ctx context.Context, r *http.Request) context.Context

Types

type FieldInfo

type FieldInfo struct {
	Name            string      `json:"name"`
	JSONName        string      `json:"jsonName,omitempty"`
	Type            string      `json:"type"`
	Kind            string      `json:"kind"`
	Required        bool        `json:"required,omitempty"`
	ValidationRules []string    `json:"validationRules,omitempty"`
	IsArray         bool        `json:"isArray,omitempty"`
	ArrayDepth      int         `json:"arrayDepth,omitempty"` // 0 = not an array, 1 = []T, 2 = [][]T, etc.
	IsPointer       bool        `json:"isPointer,omitempty"`
	PointerDepth    int         `json:"pointerDepth,omitempty"` // 0 = not a pointer, 1 = *T, 2 = **T, etc.
	ElementType     string      `json:"elementType,omitempty"`  // string representation of element type (hint)
	KeyType         string      `json:"keyType,omitempty"`      // key type for map types
	ValueType       string      `json:"valueType,omitempty"`    // value type for map types
	Fields          []FieldInfo `json:"fields,omitempty"`       // nested fields if this is a struct type
}

type FuncType added in v1.0.1

type FuncType[P, R any] func(ctx context.Context, params P) (R, error)

type Group added in v1.0.1

type Group struct {
	// contains filtered or unexported fields
}

func (*Group) Use added in v1.0.1

func (g *Group) Use(middlewares ...Middleware)

Use adds middleware to the group. Middleware added to a group will apply to all methods registered in that group.

type HandlerFunc added in v1.0.1

type HandlerFunc func(ctx context.Context, req RPCRequest) (RPCResponse, error)

type MethodInfo

type MethodInfo struct {
	Name   string `json:"name"`
	Params string `json:"params"` // name of the type
	Result string `json:"result"` // name of the type
}

type Middleware added in v1.0.1

type Middleware func(ctx context.Context, req RPCRequest, next HandlerFunc) (RPCResponse, error)

type MiddlewareChain added in v1.0.1

type MiddlewareChain struct {
	// contains filtered or unexported fields
}

func NewMiddlewareChain added in v1.0.1

func NewMiddlewareChain(middlewares ...Middleware) *MiddlewareChain

func (*MiddlewareChain) Add added in v1.0.1

func (mc *MiddlewareChain) Add(middleware Middleware)

func (*MiddlewareChain) Build added in v1.0.1

func (mc *MiddlewareChain) Build(finalHandler HandlerFunc) HandlerFunc

func (*MiddlewareChain) Len added in v1.0.1

func (mc *MiddlewareChain) Len() int

type RPCError

type RPCError struct {
	Code    int         `json:"code"`
	Message string      `json:"message"`
	Data    interface{} `json:"data,omitempty"`
}

type RPCErrorProvider

type RPCErrorProvider interface {
	Code() int
	Message() string
	Data() interface{}
}

type RPCRequest

type RPCRequest struct {
	JSONRPC string          `json:"jsonrpc"`
	Method  string          `json:"method"`
	Params  json.RawMessage `json:"params"`
	ID      json.RawMessage `json:"id"`
}

type RPCResponse

type RPCResponse struct {
	JSONRPC string          `json:"jsonrpc"`
	Result  interface{}     `json:"result,omitempty"`
	Error   *RPCError       `json:"error,omitempty"`
	ID      json.RawMessage `json:"id"`
}

type Registerer added in v1.0.1

type Registerer interface {
	// contains filtered or unexported methods
}

Both Server and Group implement this interface.

type Server

type Server struct {
	// contains filtered or unexported fields
}

func NewServer

func NewServer() *Server

func (*Server) GetMethodSpecs

func (s *Server) GetMethodSpecs() ServerSpec

GetMethodSpecs returns information about all registered RPC methods. This can be used to generate API specifications

Example usage:

spec := server.GetMethodSpecs()
for _, method := range spec.Methods {
    fmt.Printf("Method: %s\n", method.Name)
    fmt.Printf("Params: %s\n", method.Params)
    fmt.Printf("Result: %s\n", method.Result)
}
for typeName, typeInfo := range spec.Types {
    fmt.Printf("Type: %s\n", typeName)
    fmt.Printf("Fields: %+v\n", typeInfo.Fields)
}

func (*Server) Group added in v1.0.1

func (s *Server) Group(prefix string, middlewares ...Middleware) *Group

Group creates a new group with the given prefix. The prefix will be prepended to all method names registered in this group. An empty prefix is allowed. Middleware can be provided as variadic parameters and will apply to all methods in the group.

Example:

mathGroup := server.Group("math.", TimingMiddleware, AuthMiddleware())

func (*Server) SetValidateErrorHandler

func (s *Server) SetValidateErrorHandler(handler ValidateErrorHandler)

func (*Server) Use added in v1.0.1

func (s *Server) Use(middlewares ...Middleware)

type ServerSpec added in v1.0.2

type ServerSpec struct {
	Methods []MethodInfo        `json:"methods"`
	Types   map[string]TypeInfo `json:"types"`
}

type TypeInfo

type TypeInfo struct {
	Name         string      `json:"name"`              // struct name only (without package)
	Package      string      `json:"package,omitempty"` // package path
	Kind         string      `json:"kind"`
	IsArray      bool        `json:"isArray,omitempty"`
	ArrayDepth   int         `json:"arrayDepth,omitempty"` // 0 = not an array, 1 = []T, 2 = [][]T, etc.
	IsPointer    bool        `json:"isPointer,omitempty"`
	PointerDepth int         `json:"pointerDepth,omitempty"` // 0 = not a pointer, 1 = *T, 2 = **T, etc.
	ElementType  string      `json:"elementType,omitempty"`  // string representation of element type (hint)
	KeyType      string      `json:"keyType,omitempty"`      // key type for map types
	ValueType    string      `json:"valueType,omitempty"`    // value type for map types
	Fields       []FieldInfo `json:"fields,omitempty"`       // nested fields if this is a struct type
}

type UnmarshalKind added in v1.0.2

type UnmarshalKind interface {
	UnmarshalKind() string
}

UnmarshalKind is an interface that types can implement to specify their JSON unmarshaling kind. This is useful for types like types.Time or types.Duration that unmarshal from strings or numbers but are represented as structs in Go. If a type implements this interface, the returned kind will be used in the API specification instead of "struct".

type ValidateErrorHandler

type ValidateErrorHandler func(*validator.ValidationErrors) *RPCError

Directories

Path Synopsis
example
main command
math command
minimal command
tests command

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL