peekapi

package module
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Feb 28, 2026 License: MIT Imports: 24 Imported by: 0

README

PeekAPI — Go SDK

Go Reference license CI

Zero-dependency Go SDK for PeekAPI. Standard net/http middleware plus adapters for Gin, Echo, Chi, and Fiber.

Install

go get github.com/peekapi-dev/sdk-go

Quick Start

package main

import (
	"log"
	"net/http"

	peekapi "github.com/peekapi-dev/sdk-go"
)

func main() {
	client, err := peekapi.New(peekapi.Options{
		APIKey: "ak_live_xxx",
	})
	if err != nil {
		log.Fatal(err)
	}

	mux := http.NewServeMux()
	mux.HandleFunc("/api/hello", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte(`{"message":"hello"}`))
	})

	handler := peekapi.Middleware(client)(mux)
	log.Fatal(http.ListenAndServe(":8080", handler))
}

Framework Support

The core middleware uses the standard func(http.Handler) http.Handler signature. Framework-specific adapters are available as sub-packages:

Framework Package Install
net/http, Chi peekapi (core) go get github.com/peekapi-dev/sdk-go
Gin peekapigin go get github.com/peekapi-dev/sdk-go/middleware/gin
Echo peekapiecho go get github.com/peekapi-dev/sdk-go/middleware/echo
Fiber peekapifiber go get github.com/peekapi-dev/sdk-go/middleware/fiber
Chi

Chi uses the standard middleware signature natively — no extra package needed:

r := chi.NewRouter()
r.Use(peekapi.Middleware(client))
Gin
import peekapigin "github.com/peekapi-dev/sdk-go/middleware/gin"

engine := gin.Default()
engine.Use(peekapigin.Middleware(client))
Echo
import peekapiecho "github.com/peekapi-dev/sdk-go/middleware/echo"

e := echo.New()
e.Use(peekapiecho.Middleware(client))
Fiber
import peekapifiber "github.com/peekapi-dev/sdk-go/middleware/fiber"

app := fiber.New()
app.Use(peekapifiber.Middleware(client))

// With custom consumer identification
app.Use(peekapifiber.Middleware(client, peekapifiber.Options{
	IdentifyConsumer: func(c *fiber.Ctx) string {
		return c.Get("X-Tenant-ID")
	},
}))

Configuration

Field Type Default Description
APIKey string required Your PeekAPI key
Endpoint string PeekAPI cloud Ingestion endpoint URL
FlushInterval time.Duration 10s Time between automatic flushes
BatchSize int 100 Events per batch (triggers flush)
MaxBufferSize int 10,000 Max events held in memory
MaxStorageBytes int64 5MB Max disk fallback file size
Debug bool false Enable debug logging
IdentifyConsumer func(*http.Request) string auto Custom consumer ID extraction
StoragePath string temp dir JSONL fallback file path
TLSConfig *tls.Config nil Custom TLS configuration

How It Works

  1. Middleware intercepts every request/response
  2. Captures method, path, status code, response time, request/response sizes, consumer ID
  3. Events are buffered in memory and flushed in batches on a background goroutine
  4. On network failure: exponential backoff with jitter (1s, 2s, 4s, 8s, 16s)
  5. After max retries: events are persisted to a JSONL file on disk
  6. On next startup: persisted events are recovered and re-sent
  7. On SIGTERM/SIGINT: remaining buffer is flushed or persisted to disk

Consumer Identification

By default, consumers are identified by:

  1. X-API-Key header — stored as-is
  2. Authorization header — hashed with SHA-256 (stored as hash_<hex>)

Override with the IdentifyConsumer option:

client, _ := peekapi.New(peekapi.Options{
	APIKey: "ak_live_xxx",
	IdentifyConsumer: func(r *http.Request) string {
		return r.Header.Get("X-Tenant-ID")
	},
})

Graceful Shutdown

ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

if err := client.Shutdown(ctx); err != nil {
	log.Printf("shutdown error: %v", err)
}

Features

  • Zero dependencies — only Go stdlib
  • Buffered batching — in-memory buffer with configurable flush interval and batch size
  • Exponential backoff — retries with jitter on network failures
  • Disk persistence — undelivered events saved to JSONL, recovered on restart
  • SSRF protection — rejects private/loopback IP endpoints (except localhost)
  • Consumer ID hashing — Authorization headers hashed with SHA-256
  • Graceful shutdown — context-based with SIGTERM/SIGINT handlers
  • Standard middlewarefunc(http.Handler) http.Handler works with any router

Contributing

  1. Fork & clone the repo
  2. Run tests — go test ./...
  3. Lint — go vet ./...
  4. Format — gofmt -w .
  5. Submit a PR

License

MIT

Documentation

Overview

Package peekapi provides an API analytics SDK that captures HTTP request events, buffers them in memory, and flushes them in batches to an ingestion endpoint. It includes exponential backoff on failures, disk persistence for undelivered events, and SSRF protection.

Index

Constants

View Source
const DefaultEndpoint = "https://ingest.peekapi.dev/v1/events"
View Source
const Version = "0.1.0"

Version is the SDK version sent via the x-peekapi-sdk header.

Variables

View Source
var ErrEventsPersisted = fmt.Errorf("peekapi: events persisted to disk, not delivered to endpoint")

ErrEventsPersisted indicates that Shutdown could not deliver events to the ingestion endpoint and they were persisted to the local disk file instead. Callers can check for this with errors.Is(err, ErrEventsPersisted).

Functions

func DefaultIdentifyConsumer

func DefaultIdentifyConsumer(r *http.Request) string

DefaultIdentifyConsumer extracts a consumer ID from the request using the default strategy: X-API-Key header as-is, or hashed Authorization header.

func HashConsumerID

func HashConsumerID(raw string) string

HashConsumerID hashes a raw consumer identifier (e.g. Authorization header) to a short, stable, non-reversible identifier using SHA-256.

func IsPrivateIP

func IsPrivateIP(host string) bool

IsPrivateIP reports whether the given host string matches a private, loopback, or link-local IP address pattern. It checks both the string representation (regex) and the numeric value (net.IP parsing).

func Middleware

func Middleware(client *Client) func(http.Handler) http.Handler

Middleware returns a standard net/http middleware that captures request metrics and tracks them via the provided Client. It works with any Go HTTP router that supports the func(http.Handler) http.Handler pattern (stdlib mux, chi, gorilla, etc.).

Safety: if client is nil, the middleware is a no-op passthrough. Any panic during tracking (e.g. from a user-provided IdentifyConsumer callback) is recovered — analytics must never break the customer's API.

func ResolveHost

func ResolveHost(host string) (string, error)

ResolveHost resolves a hostname to its first IP address. Exported for testing.

Types

type Client

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

Client buffers request events and sends them to an ingestion endpoint.

func New

func New(opts Options) (*Client, error)

New creates a new Client with the given options. It validates the configuration, loads any previously persisted events from disk, and starts a background goroutine for periodic flushing.

func (*Client) BufferLen

func (c *Client) BufferLen() int

BufferLen returns the current number of events in the buffer (for testing).

func (*Client) Flush

func (c *Client) Flush() error

Flush sends all buffered events to the ingestion endpoint. It respects in-flight and backoff guards. Flush is safe for concurrent use. Equivalent to FlushContext(context.Background()).

func (*Client) FlushContext

func (c *Client) FlushContext(ctx context.Context) error

FlushContext sends all buffered events to the ingestion endpoint with context support for cancellation and timeouts. It respects in-flight and backoff guards. FlushContext is safe for concurrent use.

Locking strategy: mu protects flush state (flushInFlight, backoff), bufMu protects buffer/spare. Track() only acquires bufMu, so it never blocks on flush state or network I/O. Lock ordering: mu -> bufMu.

func (*Client) Shutdown

func (c *Client) Shutdown(ctx context.Context) error

Shutdown gracefully stops the client: stops the background flush goroutine, flushes remaining events, and persists any undelivered events to disk. The provided context controls the shutdown timeout.

Returns ErrEventsPersisted if events could not be delivered to the ingestion endpoint and were persisted to the local disk file instead.

func (*Client) Track

func (c *Client) Track(event RequestEvent)

Track adds an event to the buffer. If the buffer reaches BatchSize, a non-blocking flush is triggered. Track is safe for concurrent use.

func (*Client) TrackRequest

func (c *Client) TrackRequest(r *http.Request, statusCode, responseSize int, start time.Time)

TrackRequest builds a RequestEvent from the given request/response metadata and tracks it via the Client. It encapsulates duration calculation, consumer identification, request size extraction, and timestamp generation.

This method is used by the stdlib Middleware and the framework-specific middleware adapters (Gin, Echo) that wrap net/http under the hood.

Any panic from IdentifyConsumer or Track is recovered — analytics must never break the customer's API.

type Options

type Options struct {
	// APIKey is the API key used to authenticate with the ingestion endpoint (required).
	APIKey string

	// Endpoint is the URL of the ingestion endpoint. Default: PeekAPI cloud.
	Endpoint string

	// FlushInterval is the time between automatic flushes. Default: 15s.
	FlushInterval time.Duration

	// BatchSize is the number of events that triggers an automatic flush. Default: 250.
	BatchSize int

	// MaxBufferSize is the maximum number of events held in memory. Default: 10,000.
	MaxBufferSize int

	// Debug enables debug logging to stderr.
	Debug bool

	// IdentifyConsumer is an optional function to extract a consumer identifier
	// from an HTTP request. If nil, the default logic uses X-API-Key or hashes
	// the Authorization header.
	IdentifyConsumer func(r *http.Request) string

	// StoragePath is the file path for persisting undelivered events.
	// Default: os.TempDir()/peekapi-events-<hash>.jsonl
	StoragePath string

	// MaxStorageBytes is the maximum size of the storage file. Default: 5MB.
	MaxStorageBytes int64

	// TLSConfig is an optional TLS configuration for the HTTP client.
	TLSConfig *tls.Config

	// CollectQueryString includes sorted query parameters in the tracked path.
	// NOTE: increases DB usage — each unique path+query creates a separate endpoint row.
	CollectQueryString bool

	// OnError is an optional callback invoked when the background flush loop
	// encounters an error (network failure, non-retryable status, etc.).
	// Called from the background goroutine — implementations must be safe for
	// concurrent use and should not block.
	OnError func(err error)
}

Options configures the API dashboard client.

type RequestEvent

type RequestEvent struct {
	Method         string                 `json:"method"`
	Path           string                 `json:"path"`
	StatusCode     int                    `json:"status_code"`
	ResponseTimeMs float64                `json:"response_time_ms"`
	RequestSize    int                    `json:"request_size"`
	ResponseSize   int                    `json:"response_size"`
	ConsumerID     string                 `json:"consumer_id,omitempty"`
	Metadata       map[string]interface{} `json:"metadata,omitempty"`
	Timestamp      string                 `json:"timestamp"`
}

RequestEvent represents a single captured API request.

Jump to

Keyboard shortcuts

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