restheadspec

package
v1.0.2 Latest Latest
Warning

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

Go to latest
Published: Dec 30, 2025 License: MIT Imports: 25 Imported by: 0

Documentation

Overview

Package restheadspec provides the Rest Header Spec API framework.

Rest Header Spec (restheadspec) is a RESTful API framework that reads query options, filters, sorting, pagination, and other parameters from HTTP headers instead of request bodies or query parameters. This approach provides a clean separation between data and metadata in API requests.

Key Features

  • Header-based API configuration: All query options are passed via HTTP headers
  • Database-agnostic: Works with both GORM and Bun ORM through adapters
  • Router-agnostic: Supports multiple HTTP routers (Mux, BunRouter, etc.)
  • Advanced filtering: Supports complex filter operations (eq, gt, lt, like, between, etc.)
  • Pagination and sorting: Built-in support for limit, offset, and multi-column sorting
  • Preloading and expansion: Support for eager loading relationships
  • Multiple response formats: Default, simple, and Syncfusion formats

HTTP Headers

The following headers are supported for configuring API requests:

  • X-Filters: JSON array of filter conditions
  • X-Columns: Comma-separated list of columns to select
  • X-Sort: JSON array of sort specifications
  • X-Limit: Maximum number of records to return
  • X-Offset: Number of records to skip
  • X-Preload: Comma-separated list of relations to preload
  • X-Expand: Comma-separated list of relations to expand (LEFT JOIN)
  • X-Distinct: Boolean to enable DISTINCT queries
  • X-Skip-Count: Boolean to skip total count query
  • X-Response-Format: Response format (detail, simple, syncfusion)
  • X-Clean-JSON: Boolean to remove null/empty fields
  • X-Custom-SQL-Where: Custom SQL WHERE clause (AND)
  • X-Custom-SQL-Or: Custom SQL WHERE clause (OR)

Usage Example

// Create a handler with GORM
handler := restheadspec.NewHandlerWithGORM(db)

// Register models
handler.Registry.RegisterModel("users", User{})

// Setup routes with Mux
muxRouter := mux.NewRouter()
restheadspec.SetupMuxRoutes(muxRouter, handler)

// Make a request with headers
// GET /public/users
// X-Filters: [{"column":"age","operator":"gt","value":18}]
// X-Sort: [{"column":"name","direction":"asc"}]
// X-Limit: 10

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func DecodeParam

func DecodeParam(pStr string) (string, error)

DecodeParam - Decodes parameter string and returns unencoded string

func ExampleAuthorizationHook

func ExampleAuthorizationHook(ctx *HookContext) error

ExampleAuthorizationHook checks if the user has permission to perform the operation

func ExampleBunRouterWithBunDB

func ExampleBunRouterWithBunDB(bunDB *bun.DB)

ExampleBunRouterWithBunDB shows usage with both BunRouter and Bun DB

func ExampleCacheInvalidationHook

func ExampleCacheInvalidationHook(ctx *HookContext) error

ExampleCacheInvalidationHook invalidates cache after create/update/delete

func ExampleDataTransformHook

func ExampleDataTransformHook(ctx *HookContext) error

ExampleDataTransformHook modifies data before create/update

func ExampleFilterSensitiveDataHook

func ExampleFilterSensitiveDataHook(ctx *HookContext) error

ExampleFilterSensitiveDataHook removes sensitive data from responses

func ExampleRelatedDataHook

func ExampleRelatedDataHook(ctx *HookContext) error

ExampleRelatedDataHook fetches related data using the handler's database

func ExampleTxHook added in v0.0.107

func ExampleTxHook(ctx *HookContext) error

ExampleTxHook demonstrates using the Tx field to execute additional SQL queries The Tx field provides access to the database/transaction for custom queries

func ExampleValidationHook

func ExampleValidationHook(ctx *HookContext) error

ExampleValidationHook validates data before create/update operations

func ExampleWithBun

func ExampleWithBun(bunDB *bun.DB)

ExampleWithBun shows how to switch to Bun ORM

func ExampleWithGORM

func ExampleWithGORM(db *gorm.DB)

ExampleWithGORM shows how to use RestHeadSpec with GORM

func GetEntity

func GetEntity(ctx context.Context) string

GetEntity retrieves entity from context

func GetModel

func GetModel(ctx context.Context) interface{}

GetModel retrieves model from context

func GetModelPtr

func GetModelPtr(ctx context.Context) interface{}

GetModelPtr retrieves model pointer from context

func GetSchema

func GetSchema(ctx context.Context) string

GetSchema retrieves schema from context

func GetTableName

func GetTableName(ctx context.Context) string

GetTableName retrieves table name from context

func NewStandardBunRouter

func NewStandardBunRouter() *router.StandardBunRouterAdapter

NewStandardBunRouter creates a router with standard BunRouter handlers

func NewStandardMuxRouter

func NewStandardMuxRouter() *router.StandardMuxAdapter

NewStandardMuxRouter creates a router with standard Mux HTTP handlers

func RegisterSecurityHooks added in v0.0.67

func RegisterSecurityHooks(handler *Handler, securityList *security.SecurityList)

RegisterSecurityHooks registers all security-related hooks with the handler

func SetupBunRouterRoutes

func SetupBunRouterRoutes(bunRouter *router.StandardBunRouterAdapter, handler *Handler)

SetupBunRouterRoutes sets up bunrouter routes for the RestHeadSpec API

func SetupExampleHooks

func SetupExampleHooks(handler *Handler)

SetupExampleHooks demonstrates how to register hooks on a handler

func SetupMuxRoutes

func SetupMuxRoutes(muxRouter *mux.Router, handler *Handler, authMiddleware MiddlewareFunc)

SetupMuxRoutes sets up routes for the RestHeadSpec API with Mux authMiddleware is optional - if provided, routes will be protected with the middleware Example: SetupMuxRoutes(router, handler, func(h http.Handler) http.Handler { return security.NewAuthHandler(securityList, h) })

func WithEntity

func WithEntity(ctx context.Context, entity string) context.Context

WithEntity adds entity to context

func WithModel

func WithModel(ctx context.Context, model interface{}) context.Context

WithModel adds model to context

func WithModelPtr

func WithModelPtr(ctx context.Context, modelPtr interface{}) context.Context

WithModelPtr adds model pointer to context

func WithOptions added in v0.0.47

func WithOptions(ctx context.Context, options ExtendedRequestOptions) context.Context

WithOptions adds request options to context

func WithRequestData

func WithRequestData(ctx context.Context, schema, entity, tableName string, model, modelPtr interface{}, options ExtendedRequestOptions) context.Context

WithRequestData adds all request-scoped data to context at once

func WithSchema

func WithSchema(ctx context.Context, schema string) context.Context

WithSchema adds schema to context

func WithTableName

func WithTableName(ctx context.Context, tableName string) context.Context

WithTableName adds table name to context

Types

type ColumnCastInfo

type ColumnCastInfo struct {
	NeedsCast     bool
	IsNumericType bool
}

ColumnCastInfo holds information about whether a column needs casting

type CursorDirection

type CursorDirection int

CursorDirection defines pagination direction

const (
	CursorForward  CursorDirection = 1
	CursorBackward CursorDirection = -1
)

type ExpandOption

type ExpandOption struct {
	Relation string
	Columns  []string
	Where    string
	Sort     string
}

ExpandOption represents a relation expansion configuration

type ExtendedRequestOptions

type ExtendedRequestOptions struct {
	common.RequestOptions

	// Field selection
	CleanJSON bool

	// Advanced filtering
	SearchColumns  []string
	CustomSQLWhere string
	CustomSQLOr    string

	// Joins
	Expand []ExpandOption

	// Advanced features
	AdvancedSQL map[string]string // Column -> SQL expression
	ComputedQL  map[string]string // Column -> CQL expression
	Distinct    bool
	SkipCount   bool
	SkipCache   bool
	PKRow       *string

	// Response format
	ResponseFormat string // "simple", "detail", "syncfusion"

	// Single record normalization - convert single-element arrays to objects
	SingleRecordAsObject bool

	// Transaction
	AtomicTransaction bool

	// X-Files configuration - comprehensive query options as a single JSON object
	XFiles *XFiles
}

ExtendedRequestOptions extends common.RequestOptions with additional features

func GetOptions added in v0.0.47

func GetOptions(ctx context.Context) *ExtendedRequestOptions

GetOptions retrieves request options from context

func (*ExtendedRequestOptions) GetCursorFilter

func (opts *ExtendedRequestOptions) GetCursorFilter(
	tableName string,
	pkName string,
	modelColumns []string,
	expandJoins map[string]string,
) (string, error)

GetCursorFilter generates a SQL `EXISTS` subquery for cursor-based pagination. It uses the current request's sort, cursor, joins (via Expand), and CQL (via ComputedQL).

Parameters:

  • tableName: name of the main table (e.g. "post")
  • pkName: primary key column (e.g. "id")
  • modelColumns: optional list of valid main-table columns (for validation). Pass nil to skip.
  • expandJoins: optional map[alias]string of JOIN clauses (e.g. "user": "LEFT JOIN user ON ...")

Returns SQL snippet to embed in WHERE clause.

type FallbackHandler added in v0.0.69

type FallbackHandler func(w common.ResponseWriter, r common.Request, params map[string]string)

FallbackHandler is a function that handles requests when no model is found It receives the same parameters as the Handle method

type Handler

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

Handler handles API requests using database and model abstractions This handler reads filters, columns, and options from HTTP headers

func NewHandler

func NewHandler(db common.Database, registry common.ModelRegistry) *Handler

NewHandler creates a new API handler with database and registry abstractions

func NewHandlerWithBun

func NewHandlerWithBun(db *bun.DB) *Handler

NewHandlerWithBun creates a new Handler with Bun adapter

func NewHandlerWithGORM

func NewHandlerWithGORM(db *gorm.DB) *Handler

NewHandlerWithGORM creates a new Handler with GORM adapter

func (*Handler) FetchRowNumber added in v0.0.19

func (h *Handler) FetchRowNumber(ctx context.Context, tableName string, pkName string, pkValue string, options ExtendedRequestOptions, model any) (int64, error)

FetchRowNumber calculates the row number of a specific record based on sorting and filtering Returns the 1-based row number of the record with the given primary key value

func (*Handler) GetDatabase added in v0.0.65

func (h *Handler) GetDatabase() common.Database

GetDatabase returns the underlying database connection Implements common.SpecHandler interface

func (*Handler) GetRelationshipInfo added in v0.0.20

func (h *Handler) GetRelationshipInfo(modelType reflect.Type, relationName string) *common.RelationshipInfo

GetRelationshipInfo implements common.RelationshipInfoProvider interface

func (*Handler) Handle

func (h *Handler) Handle(w common.ResponseWriter, r common.Request, params map[string]string)

Handle processes API requests through router-agnostic interface Options are read from HTTP headers instead of request body

func (*Handler) HandleGet

func (h *Handler) HandleGet(w common.ResponseWriter, r common.Request, params map[string]string)

HandleGet processes GET requests for metadata

func (*Handler) HandleOpenAPI added in v0.0.87

func (h *Handler) HandleOpenAPI(w common.ResponseWriter, r common.Request)

HandleOpenAPI generates and returns the OpenAPI specification

func (*Handler) Hooks

func (h *Handler) Hooks() *HookRegistry

Hooks returns the hook registry for this handler Use this to register custom hooks for operations

func (*Handler) SetFallbackHandler added in v0.0.69

func (h *Handler) SetFallbackHandler(fallback FallbackHandler)

SetFallbackHandler sets a fallback handler to be called when no model is found If not set, the handler will simply return (pass through to next route)

func (*Handler) SetOpenAPIGenerator added in v0.0.87

func (h *Handler) SetOpenAPIGenerator(generator func() (string, error))

SetOpenAPIGenerator sets the OpenAPI generator function This allows avoiding circular dependencies

func (*Handler) ValidateAndAdjustFilterForColumnType

func (h *Handler) ValidateAndAdjustFilterForColumnType(filter *common.FilterOption, model interface{}) ColumnCastInfo

ValidateAndAdjustFilterForColumnType validates and adjusts a filter based on column type Returns ColumnCastInfo indicating whether the column should be cast to text in SQL

type HookContext

type HookContext struct {
	Context   context.Context
	Handler   *Handler // Reference to the handler for accessing database, registry, etc.
	Schema    string
	Entity    string
	TableName string
	Model     interface{}
	Options   ExtendedRequestOptions

	// Operation-specific fields
	ID          string
	Data        interface{} // For create/update operations
	Result      interface{} // For after hooks
	Error       error       // For after hooks
	QueryFilter string      // For read operations

	// Query chain - allows hooks to modify the query before execution
	// Can be SelectQuery, InsertQuery, UpdateQuery, or DeleteQuery
	Query interface{}

	// Response writer - allows hooks to modify response
	Writer common.ResponseWriter

	// Tx provides access to the database/transaction for executing additional SQL
	// This allows hooks to run custom queries in addition to the main Query chain
	Tx common.Database
}

HookContext contains all the data available to a hook

type HookFunc

type HookFunc func(*HookContext) error

HookFunc is the signature for hook functions It receives a HookContext and can modify it or return an error If an error is returned, the operation will be aborted

func ExampleAuditLogHook

func ExampleAuditLogHook(hookType HookType) HookFunc

ExampleAuditLogHook creates audit log entries for operations

func ExampleLoggingHook

func ExampleLoggingHook(hookType HookType) HookFunc

ExampleLoggingHook logs before and after operations

type HookRegistry

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

HookRegistry manages all registered hooks

func NewHookRegistry

func NewHookRegistry() *HookRegistry

NewHookRegistry creates a new hook registry

func (*HookRegistry) Clear

func (r *HookRegistry) Clear(hookType HookType)

Clear removes all hooks for the specified type

func (*HookRegistry) ClearAll

func (r *HookRegistry) ClearAll()

ClearAll removes all registered hooks

func (*HookRegistry) Count

func (r *HookRegistry) Count(hookType HookType) int

Count returns the number of hooks registered for a specific type

func (*HookRegistry) Execute

func (r *HookRegistry) Execute(hookType HookType, ctx *HookContext) error

Execute runs all hooks for the specified type in order If any hook returns an error, execution stops and the error is returned

func (*HookRegistry) GetAllHookTypes

func (r *HookRegistry) GetAllHookTypes() []HookType

GetAllHookTypes returns all hook types that have registered hooks

func (*HookRegistry) HasHooks

func (r *HookRegistry) HasHooks(hookType HookType) bool

HasHooks returns true if there are any hooks registered for the specified type

func (*HookRegistry) Register

func (r *HookRegistry) Register(hookType HookType, hook HookFunc)

Register adds a new hook for the specified hook type

func (*HookRegistry) RegisterMultiple

func (r *HookRegistry) RegisterMultiple(hookTypes []HookType, hook HookFunc)

RegisterMultiple registers a hook for multiple hook types

type HookType

type HookType string

HookType defines the type of hook to execute

const (
	// Read operation hooks
	BeforeRead HookType = "before_read"
	AfterRead  HookType = "after_read"

	// Create operation hooks
	BeforeCreate HookType = "before_create"
	AfterCreate  HookType = "after_create"

	// Update operation hooks
	BeforeUpdate HookType = "before_update"
	AfterUpdate  HookType = "after_update"

	// Delete operation hooks
	BeforeDelete HookType = "before_delete"
	AfterDelete  HookType = "after_delete"

	// Scan/Execute operation hooks
	BeforeScan HookType = "before_scan"
)

type MiddlewareFunc added in v0.0.65

type MiddlewareFunc func(http.Handler) http.Handler

MiddlewareFunc is a function that wraps an http.Handler with additional functionality

type XFiles added in v0.0.46

type XFiles struct {
	TableName   string      `json:"tablename"`
	Schema      string      `json:"schema"`
	PrimaryKey  string      `json:"primarykey"`
	ForeignKey  string      `json:"foreignkey"`
	RelatedKey  string      `json:"relatedkey"`
	Sort        []string    `json:"sort"`
	Prefix      string      `json:"prefix"`
	Editable    bool        `json:"editable"`
	Recursive   bool        `json:"recursive"`
	Expand      bool        `json:"expand"`
	Rownumber   bool        `json:"rownumber"`
	Skipcount   bool        `json:"skipcount"`
	Offset      json.Number `json:"offset"`
	Limit       json.Number `json:"limit"`
	Columns     []string    `json:"columns"`
	OmitColumns []string    `json:"omit_columns"`
	CQLColumns  []string    `json:"cql_columns"`

	SqlJoins     []string     `json:"sql_joins"`
	SqlOr        []string     `json:"sql_or"`
	SqlAnd       []string     `json:"sql_and"`
	ParentTables []*XFiles    `json:"parenttables"`
	ChildTables  []*XFiles    `json:"childtables"`
	ModelType    reflect.Type `json:"-"`
	ParentEntity *XFiles      `json:"-"`
	Level        uint         `json:"-"`
	Errors       []error      `json:"-"`
	FilterFields []struct {
		Field    string `json:"field"`
		Value    string `json:"value"`
		Operator string `json:"operator"`
	} `json:"filter_fields"`
	CursorForward  string `json:"cursor_forward"`
	CursorBackward string `json:"cursor_backward"`
}

Jump to

Keyboard shortcuts

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