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 ¶
- func DecodeParam(pStr string) (string, error)
- func ExampleAuthorizationHook(ctx *HookContext) error
- func ExampleBunRouterWithBunDB(bunDB *bun.DB)
- func ExampleCacheInvalidationHook(ctx *HookContext) error
- func ExampleDataTransformHook(ctx *HookContext) error
- func ExampleFilterSensitiveDataHook(ctx *HookContext) error
- func ExampleRelatedDataHook(ctx *HookContext) error
- func ExampleTxHook(ctx *HookContext) error
- func ExampleValidationHook(ctx *HookContext) error
- func ExampleWithBun(bunDB *bun.DB)
- func ExampleWithGORM(db *gorm.DB)
- func GetEntity(ctx context.Context) string
- func GetModel(ctx context.Context) interface{}
- func GetModelPtr(ctx context.Context) interface{}
- func GetSchema(ctx context.Context) string
- func GetTableName(ctx context.Context) string
- func NewStandardBunRouter() *router.StandardBunRouterAdapter
- func NewStandardMuxRouter() *router.StandardMuxAdapter
- func RegisterSecurityHooks(handler *Handler, securityList *security.SecurityList)
- func SetupBunRouterRoutes(bunRouter *router.StandardBunRouterAdapter, handler *Handler)
- func SetupExampleHooks(handler *Handler)
- func SetupMuxRoutes(muxRouter *mux.Router, handler *Handler, authMiddleware MiddlewareFunc)
- func WithEntity(ctx context.Context, entity string) context.Context
- func WithModel(ctx context.Context, model interface{}) context.Context
- func WithModelPtr(ctx context.Context, modelPtr interface{}) context.Context
- func WithOptions(ctx context.Context, options ExtendedRequestOptions) context.Context
- func WithRequestData(ctx context.Context, schema, entity, tableName string, ...) context.Context
- func WithSchema(ctx context.Context, schema string) context.Context
- func WithTableName(ctx context.Context, tableName string) context.Context
- type ColumnCastInfo
- type CursorDirection
- type ExpandOption
- type ExtendedRequestOptions
- type FallbackHandler
- type Handler
- func (h *Handler) FetchRowNumber(ctx context.Context, tableName string, pkName string, pkValue string, ...) (int64, error)
- func (h *Handler) GetDatabase() common.Database
- func (h *Handler) GetRelationshipInfo(modelType reflect.Type, relationName string) *common.RelationshipInfo
- func (h *Handler) Handle(w common.ResponseWriter, r common.Request, params map[string]string)
- func (h *Handler) HandleGet(w common.ResponseWriter, r common.Request, params map[string]string)
- func (h *Handler) HandleOpenAPI(w common.ResponseWriter, r common.Request)
- func (h *Handler) Hooks() *HookRegistry
- func (h *Handler) SetFallbackHandler(fallback FallbackHandler)
- func (h *Handler) SetOpenAPIGenerator(generator func() (string, error))
- func (h *Handler) ValidateAndAdjustFilterForColumnType(filter *common.FilterOption, model interface{}) ColumnCastInfo
- type HookContext
- type HookFunc
- type HookRegistry
- func (r *HookRegistry) Clear(hookType HookType)
- func (r *HookRegistry) ClearAll()
- func (r *HookRegistry) Count(hookType HookType) int
- func (r *HookRegistry) Execute(hookType HookType, ctx *HookContext) error
- func (r *HookRegistry) GetAllHookTypes() []HookType
- func (r *HookRegistry) HasHooks(hookType HookType) bool
- func (r *HookRegistry) Register(hookType HookType, hook HookFunc)
- func (r *HookRegistry) RegisterMultiple(hookTypes []HookType, hook HookFunc)
- type HookType
- type MiddlewareFunc
- type XFiles
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func DecodeParam ¶
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 ¶
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 ¶
ExampleWithBun shows how to switch to Bun ORM
func ExampleWithGORM ¶
ExampleWithGORM shows how to use RestHeadSpec with GORM
func GetModelPtr ¶
GetModelPtr retrieves model pointer from context
func GetTableName ¶
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 ¶
WithEntity adds entity to context
func WithModelPtr ¶
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 ¶
WithSchema adds schema to context
Types ¶
type ColumnCastInfo ¶
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 ¶
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
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 ¶
NewHandlerWithBun creates a new Handler with Bun adapter
func NewHandlerWithGORM ¶
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
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 ¶
Handle processes API requests through router-agnostic interface Options are read from HTTP headers instead of request body
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
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 ¶
ExampleAuditLogHook creates audit log entries for operations
func ExampleLoggingHook ¶
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
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"`
}