Documentation
¶
Overview ¶
Package api is a platform module: a gRPC server and a REST gateway in front of it, with the interceptors every service needs and the OpenAPI description of the API.
The project writes proto files and handlers and registers its services from wireDomain; the module owns the servers, the interceptor chain, the gateway options, health, metrics, CORS and the documentation page.
Index ¶
- Constants
- Variables
- func AddStreamInterceptor(app *platform.App, i grpc.StreamServerInterceptor)
- func AddUnaryInterceptor(app *platform.App, i grpc.UnaryServerInterceptor)
- func Authorize(app *platform.App, unary grpc.UnaryServerInterceptor, ...)
- func BodyTooLarge(r *http.Request) bool
- func ClientIP(ctx context.Context) string
- func HTTPErrorHandler(app *platform.App, h runtime.ErrorHandlerFunc)
- func HandleHTTP(app *platform.App, pattern string, h http.Handler)
- func IdempotencyUser(app *platform.App, user func(ctx context.Context) string)
- func Methods(app *platform.App) []string
- func PublicMethods(app *platform.App, public func(method string) bool)
- func Register(app *platform.App, s Service)
- func RequestID(ctx context.Context) string
- func RequireIdempotency(app *platform.App, methods ...string)
- func UseHTTP(app *platform.App, mw func(http.Handler) http.Handler)
- type Config
- type IdempotencyRecord
- type IdempotencyStore
- type MemoryIdempotencyStore
- func (s *MemoryIdempotencyStore) Begin(_ context.Context, rec IdempotencyRecord) (IdempotencyRecord, bool, error)
- func (s *MemoryIdempotencyStore) DeleteExpired(_ context.Context, now time.Time) (int, error)
- func (s *MemoryIdempotencyStore) Finish(_ context.Context, rec IdempotencyRecord) error
- func (s *MemoryIdempotencyStore) Retake(_ context.Context, id int64, lockUntil time.Time) (bool, error)
- type Module
- func (m *Module) GRPCAddr() string
- func (m *Module) HTTPAddr() string
- func (m *Module) Health(context.Context) error
- func (m *Module) Init(_ context.Context, app *platform.App) error
- func (m *Module) Name() string
- func (m *Module) Start(ctx context.Context) error
- func (m *Module) Stop(ctx context.Context) error
- type Option
- type RateLimit
- type Registry
- type RetryableError
- type Service
Constants ¶
const IdempotencyKeyHeader = "Idempotency-Key"
IdempotencyKeyHeader is the header a client sends to make a call safe to repeat.
Variables ¶
var DefaultTrustedProxies = []netip.Prefix{ netip.MustParsePrefix("127.0.0.0/8"), netip.MustParsePrefix("10.0.0.0/8"), netip.MustParsePrefix("172.16.0.0/12"), netip.MustParsePrefix("192.168.0.0/16"), netip.MustParsePrefix("::1/128"), netip.MustParsePrefix("fc00::/7"), }
DefaultTrustedProxies are loopback and the private networks.
Functions ¶
func AddStreamInterceptor ¶
func AddStreamInterceptor(app *platform.App, i grpc.StreamServerInterceptor)
AddStreamInterceptor adds a project stream interceptor.
func AddUnaryInterceptor ¶
func AddUnaryInterceptor(app *platform.App, i grpc.UnaryServerInterceptor)
AddUnaryInterceptor adds a project interceptor, such as authentication. Project interceptors run after recovery, metrics and logging and before request validation.
func Authorize ¶ added in v0.3.0
func Authorize(app *platform.App, unary grpc.UnaryServerInterceptor, stream grpc.StreamServerInterceptor)
Authorize adds an access check. It runs after every project interceptor — so the caller's identity is already in the context — and before request validation. The rbac module registers itself here.
func BodyTooLarge ¶ added in v0.4.0
BodyTooLarge reports whether the request body is over API_MAX_RECV_SIZE: declared larger, or found larger while it was read. A project middleware that reads the body itself, such as a signature check, answers 413 when it is.
func ClientIP ¶ added in v0.3.0
ClientIP returns the address of the client. Behind a proxy it is the last address of X-Forwarded-For — the one the proxy itself saw, which the client cannot forge — and the connection address otherwise. The gateway passes it on to gRPC handlers.
func HTTPErrorHandler ¶ added in v0.4.0
func HTTPErrorHandler(app *platform.App, h runtime.ErrorHandlerFunc)
HTTPErrorHandler replaces how the REST side writes an error, for an API whose contract has its own error body. It receives every error of the gateway: from the handlers, from request decoding and routing, and a body over the limit as a *runtime.HTTPStatusError with 413. A later call replaces an earlier one.
func HandleHTTP ¶
HandleHTTP serves a plain HTTP route next to the gateway: webhooks, file downloads, anything that is not a gRPC call.
func IdempotencyUser ¶ added in v0.3.0
IdempotencyUser scopes idempotency keys to the caller: two users may use the same key. The project's authentication supplies the user.
func Methods ¶ added in v0.3.0
Methods returns the full names of every registered gRPC method, such as /shop.v1.OrdersService/CreateOrder. It is filled when the module starts.
func PublicMethods ¶ added in v0.3.0
PublicMethods tells the API which methods are public, for their separate rate limit. The rbac module sets it from its policy.
func RequestID ¶ added in v0.3.0
RequestID returns the id of the request: the X-Request-ID the client sent, or one generated for it. It works for HTTP handlers and for gRPC handlers behind the gateway.
func RequireIdempotency ¶ added in v0.3.0
RequireIdempotency makes the methods refuse a call without an Idempotency-Key header, as taply does for every method that creates something.
Types ¶
type Config ¶
type Config struct {
GRPCAddr string // gRPC listen address
HTTPAddr string // REST gateway listen address
MaxRecvSize int // largest request message, bytes
MaxSendSize int // largest response message, bytes
CORSOrigins []string // origins allowed to call the REST API from a browser; "*" allows any
Docs bool // serve /openapi.yaml and the /docs page
Reflection bool // gRPC server reflection, for grpcurl and similar tools
// RateLimit per client address; zero rates turn it off.
RateLimit RateLimit
// Idempotency: how long a finished call is remembered, and how long a running one
// holds its key before a repeat may take over.
IdempotencyRetention time.Duration
IdempotencyLock time.Duration
AccessLog bool // log every HTTP request, as taply does
LogBodies bool // put the bodies of failed requests into the access log
SecurityHeaders bool // taply's security response headers
// TrustedProxies are the networks whose X-Forwarded-For is believed. A request from
// anywhere else is identified by its connection address, so a client reaching the
// service directly cannot pose as another address. Empty means the private networks
// and loopback, where a reverse proxy normally runs.
TrustedProxies []netip.Prefix
}
Config holds module settings. Load fills it from the environment; the platform generator writes the Load call into the project's config.gen.go.
type IdempotencyRecord ¶ added in v0.3.0
type IdempotencyRecord struct {
ID int64
User string
Method string
Key string
Fingerprint string
Status string
Code int32
Message string
Response []byte
ExpiresAt time.Time
}
IdempotencyRecord is one remembered call.
type IdempotencyStore ¶ added in v0.3.0
type IdempotencyStore interface {
// Begin inserts an in_progress record, or returns the existing one with created false.
Begin(ctx context.Context, rec IdempotencyRecord) (IdempotencyRecord, bool, error)
// Retake moves a retry record, or an in_progress one whose lock expired, back to
// in_progress. It reports false when another call took it first.
Retake(ctx context.Context, id int64, lockUntil time.Time) (bool, error)
// Finish stores the outcome.
Finish(ctx context.Context, rec IdempotencyRecord) error
// DeleteExpired removes records past their expiry.
DeleteExpired(ctx context.Context, now time.Time) (int, error)
}
IdempotencyStore keeps the records. The postgres store is used when the postgres module is enabled; tests use the memory one.
func NewPostgresIdempotencyStore ¶ added in v0.3.0
NewPostgresIdempotencyStore creates the table if needed and returns the store the module uses with the postgres module.
type MemoryIdempotencyStore ¶ added in v0.3.0
type MemoryIdempotencyStore struct {
// contains filtered or unexported fields
}
MemoryIdempotencyStore keeps records in memory. It does not survive a restart and is not shared between instances: it is for tests.
func NewMemoryIdempotencyStore ¶ added in v0.3.0
func NewMemoryIdempotencyStore() *MemoryIdempotencyStore
NewMemoryIdempotencyStore creates an empty store.
func (*MemoryIdempotencyStore) Begin ¶ added in v0.3.0
func (s *MemoryIdempotencyStore) Begin(_ context.Context, rec IdempotencyRecord) (IdempotencyRecord, bool, error)
func (*MemoryIdempotencyStore) DeleteExpired ¶ added in v0.3.0
func (*MemoryIdempotencyStore) Finish ¶ added in v0.3.0
func (s *MemoryIdempotencyStore) Finish(_ context.Context, rec IdempotencyRecord) error
type Module ¶
type Module struct {
// contains filtered or unexported fields
}
Module implements platform.Module.
func (*Module) Init ¶
Init puts the registry into the container, so wireDomain can register services.
type Option ¶
type Option func(*Module)
Option configures the module.
func WithIdempotencyStore ¶ added in v0.3.0
func WithIdempotencyStore(store IdempotencyStore) Option
WithIdempotencyStore sets where idempotency records are kept. Without it the module uses the database of the postgres module; tests pass a memory store.
func WithOpenAPI ¶
WithOpenAPI gives the module the generated OpenAPI description: a directory holding openapi.yaml. The generated wiring passes the embedded api/openapi directory.
type RateLimit ¶ added in v0.3.0
RateLimit is a token bucket per client address, with separate limits for public methods, as in taply.
type Registry ¶
type Registry struct {
// contains filtered or unexported fields
}
Registry holds what the project adds to the API. It lives in the container and is filled from wireDomain, before the module starts.
type RetryableError ¶ added in v0.3.0
type RetryableError interface{ IsRetryable() bool }
RetryableError marks an error after which a repeated call must run again instead of getting the cached failure: a timeout of an external system, a lost connection.
type Service ¶
type Service struct {
GRPC func(*grpc.Server)
Gateway func(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error
}
Service is a gRPC service of the project and, optionally, its REST gateway. Both functions are generated: RegisterXServer from protoc-gen-go-grpc and RegisterXHandler from protoc-gen-grpc-gateway.
api.Register(app, api.Service{
GRPC: func(s *grpc.Server) { ordersv1.RegisterOrdersServiceServer(s, handler) },
Gateway: ordersv1.RegisterOrdersServiceHandler,
})