mirror of
https://github.com/bitechdev/ResolveSpec.git
synced 2026-01-03 10:24:26 +00:00
A lot more tests
This commit is contained in:
514
README.md
514
README.md
@@ -1,81 +1,83 @@
|
||||
# 📜 ResolveSpec 📜
|
||||
|
||||

|
||||

|
||||
|
||||
ResolveSpec is a flexible and powerful REST API specification and implementation that provides GraphQL-like capabilities while maintaining REST simplicity. It offers **two complementary approaches**:
|
||||
|
||||
1. **ResolveSpec** - Body-based API with JSON request options
|
||||
2. **RestHeadSpec** - Header-based API where query options are passed via HTTP headers
|
||||
3. **FuncSpec** - Header-based API to map and call API's to sql functions.
|
||||
|
||||
Both share the same core architecture and provide dynamic data querying, relationship preloading, and complex filtering.
|
||||
|
||||
**🆕 New in v2.0**: Database-agnostic architecture with support for GORM, Bun, and other ORMs. Router-flexible design works with Gorilla Mux, Gin, Echo, and more.
|
||||
Documentation Generated by LLMs
|
||||
|
||||
**🆕 New in v2.1**: RestHeadSpec (HeaderSpec) - Header-based REST API with lifecycle hooks, cursor pagination, and advanced filtering.
|
||||
|
||||
**🆕 New in v3.0**: Explicit route registration - Routes are now created per registered model for better flexibility and control. OPTIONS method support with full CORS headers for cross-origin requests.
|
||||
|
||||

|
||||

|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Features](#features)
|
||||
- [Installation](#installation)
|
||||
- [Quick Start](#quick-start)
|
||||
- [ResolveSpec (Body-Based API)](#resolvespec-body-based-api)
|
||||
- [RestHeadSpec (Header-Based API)](#restheadspec-header-based-api)
|
||||
- [Existing Code (Backward Compatible)](#option-1-existing-code-backward-compatible)
|
||||
- [New Database-Agnostic API](#option-2-new-database-agnostic-api)
|
||||
- [Router Integration](#router-integration)
|
||||
- [Migration from v1.x](#migration-from-v1x)
|
||||
- [Architecture](#architecture)
|
||||
- [API Structure](#api-structure)
|
||||
- [RestHeadSpec: Header-Based API](#restheadspec-header-based-api-1)
|
||||
- [Lifecycle Hooks](#lifecycle-hooks)
|
||||
- [Cursor Pagination](#cursor-pagination)
|
||||
- [Response Formats](#response-formats)
|
||||
- [Single Record as Object](#single-record-as-object-default-behavior)
|
||||
- [Example Usage](#example-usage)
|
||||
- [Recursive CRUD Operations](#recursive-crud-operations-)
|
||||
- [Testing](#testing)
|
||||
- [What's New](#whats-new)
|
||||
* [Features](#features)
|
||||
* [Installation](#installation)
|
||||
* [Quick Start](#quick-start)
|
||||
* [ResolveSpec (Body-Based API)](#resolvespec-body-based-api)
|
||||
* [RestHeadSpec (Header-Based API)](#restheadspec-header-based-api)
|
||||
* [Existing Code (Backward Compatible)](#option-1-existing-code-backward-compatible)
|
||||
* [New Database-Agnostic API](#option-2-new-database-agnostic-api)
|
||||
* [Router Integration](#router-integration)
|
||||
* [Migration from v1.x](#migration-from-v1x)
|
||||
* [Architecture](#architecture)
|
||||
* [API Structure](#api-structure)
|
||||
* [RestHeadSpec: Header-Based API](#restheadspec-header-based-api-1)
|
||||
* [Lifecycle Hooks](#lifecycle-hooks)
|
||||
* [Cursor Pagination](#cursor-pagination)
|
||||
* [Response Formats](#response-formats)
|
||||
* [Single Record as Object](#single-record-as-object-default-behavior)
|
||||
* [Example Usage](#example-usage)
|
||||
* [Recursive CRUD Operations](#recursive-crud-operations-)
|
||||
* [Testing](#testing)
|
||||
* [What's New](#whats-new)
|
||||
|
||||
## Features
|
||||
|
||||
### Core Features
|
||||
- **Dynamic Data Querying**: Select specific columns and relationships to return
|
||||
- **Relationship Preloading**: Load related entities with custom column selection and filters
|
||||
- **Complex Filtering**: Apply multiple filters with various operators
|
||||
- **Sorting**: Multi-column sort support
|
||||
- **Pagination**: Built-in limit/offset and cursor-based pagination
|
||||
- **Computed Columns**: Define virtual columns for complex calculations
|
||||
- **Custom Operators**: Add custom SQL conditions when needed
|
||||
- **🆕 Recursive CRUD Handler**: Automatically handle nested object graphs with foreign key resolution and per-record operation control via `_request` field
|
||||
|
||||
* **Dynamic Data Querying**: Select specific columns and relationships to return
|
||||
* **Relationship Preloading**: Load related entities with custom column selection and filters
|
||||
* **Complex Filtering**: Apply multiple filters with various operators
|
||||
* **Sorting**: Multi-column sort support
|
||||
* **Pagination**: Built-in limit/offset and cursor-based pagination
|
||||
* **Computed Columns**: Define virtual columns for complex calculations
|
||||
* **Custom Operators**: Add custom SQL conditions when needed
|
||||
* **🆕 Recursive CRUD Handler**: Automatically handle nested object graphs with foreign key resolution and per-record operation control via `_request` field
|
||||
|
||||
### Architecture (v2.0+)
|
||||
- **🆕 Database Agnostic**: Works with GORM, Bun, or any database layer through adapters
|
||||
- **🆕 Router Flexible**: Integrates with Gorilla Mux, Gin, Echo, or custom routers
|
||||
- **🆕 Backward Compatible**: Existing code works without changes
|
||||
- **🆕 Better Testing**: Mockable interfaces for easy unit testing
|
||||
|
||||
* **🆕 Database Agnostic**: Works with GORM, Bun, or any database layer through adapters
|
||||
* **🆕 Router Flexible**: Integrates with Gorilla Mux, Gin, Echo, or custom routers
|
||||
* **🆕 Backward Compatible**: Existing code works without changes
|
||||
* **🆕 Better Testing**: Mockable interfaces for easy unit testing
|
||||
|
||||
### RestHeadSpec (v2.1+)
|
||||
- **🆕 Header-Based API**: All query options passed via HTTP headers instead of request body
|
||||
- **🆕 Lifecycle Hooks**: Before/after hooks for create, read, update, and delete operations
|
||||
- **🆕 Cursor Pagination**: Efficient cursor-based pagination with complex sort support
|
||||
- **🆕 Multiple Response Formats**: Simple, detailed, and Syncfusion-compatible formats
|
||||
- **🆕 Single Record as Object**: Automatically normalize single-element arrays to objects (enabled by default)
|
||||
- **🆕 Advanced Filtering**: Field filters, search operators, AND/OR logic, and custom SQL
|
||||
- **🆕 Base64 Encoding**: Support for base64-encoded header values
|
||||
|
||||
* **🆕 Header-Based API**: All query options passed via HTTP headers instead of request body
|
||||
* **🆕 Lifecycle Hooks**: Before/after hooks for create, read, update, and delete operations
|
||||
* **🆕 Cursor Pagination**: Efficient cursor-based pagination with complex sort support
|
||||
* **🆕 Multiple Response Formats**: Simple, detailed, and Syncfusion-compatible formats
|
||||
* **🆕 Single Record as Object**: Automatically normalize single-element arrays to objects (enabled by default)
|
||||
* **🆕 Advanced Filtering**: Field filters, search operators, AND/OR logic, and custom SQL
|
||||
* **🆕 Base64 Encoding**: Support for base64-encoded header values
|
||||
|
||||
### Routing & CORS (v3.0+)
|
||||
- **🆕 Explicit Route Registration**: Routes created per registered model instead of dynamic lookups
|
||||
- **🆕 OPTIONS Method Support**: Full OPTIONS method support returning model metadata
|
||||
- **🆕 CORS Headers**: Comprehensive CORS support with all HeadSpec headers allowed
|
||||
- **🆕 Better Route Control**: Customize routes per model with more flexibility
|
||||
|
||||
* **🆕 Explicit Route Registration**: Routes created per registered model instead of dynamic lookups
|
||||
* **🆕 OPTIONS Method Support**: Full OPTIONS method support returning model metadata
|
||||
* **🆕 CORS Headers**: Comprehensive CORS support with all HeadSpec headers allowed
|
||||
* **🆕 Better Route Control**: Customize routes per model with more flexibility
|
||||
|
||||
## API Structure
|
||||
|
||||
### URL Patterns
|
||||
|
||||
```
|
||||
/[schema]/[table_or_entity]/[id]
|
||||
/[schema]/[table_or_entity]
|
||||
@@ -85,7 +87,7 @@ Both share the same core architecture and provide dynamic data querying, relatio
|
||||
|
||||
### Request Format
|
||||
|
||||
```json
|
||||
```JSON
|
||||
{
|
||||
"operation": "read|create|update|delete",
|
||||
"data": {
|
||||
@@ -110,7 +112,7 @@ RestHeadSpec provides an alternative REST API approach where all query options a
|
||||
|
||||
### Quick Example
|
||||
|
||||
```http
|
||||
```HTTP
|
||||
GET /public/users HTTP/1.1
|
||||
Host: api.example.com
|
||||
X-Select-Fields: id,name,email,department_id
|
||||
@@ -124,7 +126,7 @@ X-DetailApi: true
|
||||
|
||||
### Setup with GORM
|
||||
|
||||
```go
|
||||
```Go
|
||||
import "github.com/bitechdev/ResolveSpec/pkg/restheadspec"
|
||||
import "github.com/gorilla/mux"
|
||||
|
||||
@@ -147,7 +149,7 @@ http.ListenAndServe(":8080", router)
|
||||
|
||||
### Setup with Bun ORM
|
||||
|
||||
```go
|
||||
```Go
|
||||
import "github.com/bitechdev/ResolveSpec/pkg/restheadspec"
|
||||
import "github.com/uptrace/bun"
|
||||
|
||||
@@ -164,19 +166,19 @@ restheadspec.SetupMuxRoutes(router, handler)
|
||||
|
||||
### Common Headers
|
||||
|
||||
| Header | Description | Example |
|
||||
|--------|-------------|---------|
|
||||
| `X-Select-Fields` | Columns to include | `id,name,email` |
|
||||
| `X-Not-Select-Fields` | Columns to exclude | `password,internal_notes` |
|
||||
| `X-FieldFilter-{col}` | Exact match filter | `X-FieldFilter-Status: active` |
|
||||
| `X-SearchFilter-{col}` | Fuzzy search (ILIKE) | `X-SearchFilter-Name: john` |
|
||||
| `X-SearchOp-{op}-{col}` | Filter with operator | `X-SearchOp-Gte-Age: 18` |
|
||||
| `X-Preload` | Preload relations | `posts:id,title` |
|
||||
| `X-Sort` | Sort columns | `-created_at,+name` |
|
||||
| `X-Limit` | Limit results | `50` |
|
||||
| `X-Offset` | Offset for pagination | `100` |
|
||||
| `X-Clean-JSON` | Remove null/empty fields | `true` |
|
||||
| `X-Single-Record-As-Object` | Return single records as objects (default: `true`) | `false` |
|
||||
| Header | Description | Example |
|
||||
| --------------------------- | -------------------------------------------------- | ------------------------------ |
|
||||
| `X-Select-Fields` | Columns to include | `id,name,email` |
|
||||
| `X-Not-Select-Fields` | Columns to exclude | `password,internal_notes` |
|
||||
| `X-FieldFilter-{col}` | Exact match filter | `X-FieldFilter-Status: active` |
|
||||
| `X-SearchFilter-{col}` | Fuzzy search (ILIKE) | `X-SearchFilter-Name: john` |
|
||||
| `X-SearchOp-{op}-{col}` | Filter with operator | `X-SearchOp-Gte-Age: 18` |
|
||||
| `X-Preload` | Preload relations | `posts:id,title` |
|
||||
| `X-Sort` | Sort columns | `-created_at,+name` |
|
||||
| `X-Limit` | Limit results | `50` |
|
||||
| `X-Offset` | Offset for pagination | `100` |
|
||||
| `X-Clean-JSON` | Remove null/empty fields | `true` |
|
||||
| `X-Single-Record-As-Object` | Return single records as objects (default: `true`) | `false` |
|
||||
|
||||
**Available Operators**: `eq`, `neq`, `gt`, `gte`, `lt`, `lte`, `contains`, `startswith`, `endswith`, `between`, `betweeninclusive`, `in`, `empty`, `notempty`
|
||||
|
||||
@@ -187,11 +189,14 @@ For complete header documentation, see [pkg/restheadspec/HEADERS.md](pkg/resthea
|
||||
ResolveSpec and RestHeadSpec include comprehensive CORS support for cross-origin requests:
|
||||
|
||||
**OPTIONS Method**:
|
||||
```http
|
||||
|
||||
```HTTP
|
||||
OPTIONS /public/users HTTP/1.1
|
||||
```
|
||||
|
||||
Returns metadata with appropriate CORS headers:
|
||||
```http
|
||||
|
||||
```HTTP
|
||||
Access-Control-Allow-Origin: *
|
||||
Access-Control-Allow-Methods: GET, POST, OPTIONS
|
||||
Access-Control-Allow-Headers: Content-Type, Authorization, X-Select-Fields, X-FieldFilter-*, ...
|
||||
@@ -200,14 +205,16 @@ Access-Control-Allow-Credentials: true
|
||||
```
|
||||
|
||||
**Key Features**:
|
||||
- OPTIONS returns model metadata (same as GET metadata endpoint)
|
||||
- All HTTP methods include CORS headers automatically
|
||||
- OPTIONS requests don't require authentication (CORS preflight)
|
||||
- Supports all HeadSpec custom headers (`X-Select-Fields`, `X-FieldFilter-*`, etc.)
|
||||
- 24-hour max age to reduce preflight requests
|
||||
|
||||
* OPTIONS returns model metadata (same as GET metadata endpoint)
|
||||
* All HTTP methods include CORS headers automatically
|
||||
* OPTIONS requests don't require authentication (CORS preflight)
|
||||
* Supports all HeadSpec custom headers (`X-Select-Fields`, `X-FieldFilter-*`, etc.)
|
||||
* 24-hour max age to reduce preflight requests
|
||||
|
||||
**Configuration**:
|
||||
```go
|
||||
|
||||
```Go
|
||||
import "github.com/bitechdev/ResolveSpec/pkg/common"
|
||||
|
||||
// Get default CORS config
|
||||
@@ -222,7 +229,7 @@ corsConfig.AllowedMethods = []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"}
|
||||
|
||||
RestHeadSpec supports lifecycle hooks for all CRUD operations:
|
||||
|
||||
```go
|
||||
```Go
|
||||
import "github.com/bitechdev/ResolveSpec/pkg/restheadspec"
|
||||
|
||||
// Create handler
|
||||
@@ -267,27 +274,29 @@ handler.Hooks.Register(restheadspec.BeforeCreate, func(ctx *restheadspec.HookCon
|
||||
```
|
||||
|
||||
**Available Hook Types**:
|
||||
- `BeforeRead`, `AfterRead`
|
||||
- `BeforeCreate`, `AfterCreate`
|
||||
- `BeforeUpdate`, `AfterUpdate`
|
||||
- `BeforeDelete`, `AfterDelete`
|
||||
|
||||
* `BeforeRead`, `AfterRead`
|
||||
* `BeforeCreate`, `AfterCreate`
|
||||
* `BeforeUpdate`, `AfterUpdate`
|
||||
* `BeforeDelete`, `AfterDelete`
|
||||
|
||||
**HookContext** provides:
|
||||
- `Context`: Request context
|
||||
- `Handler`: Access to handler, database, and registry
|
||||
- `Schema`, `Entity`, `TableName`: Request info
|
||||
- `Model`: The registered model type
|
||||
- `Options`: Parsed request options (filters, sorting, etc.)
|
||||
- `ID`: Record ID (for single-record operations)
|
||||
- `Data`: Request data (for create/update)
|
||||
- `Result`: Operation result (for after hooks)
|
||||
- `Writer`: Response writer (allows hooks to modify response)
|
||||
|
||||
* `Context`: Request context
|
||||
* `Handler`: Access to handler, database, and registry
|
||||
* `Schema`, `Entity`, `TableName`: Request info
|
||||
* `Model`: The registered model type
|
||||
* `Options`: Parsed request options (filters, sorting, etc.)
|
||||
* `ID`: Record ID (for single-record operations)
|
||||
* `Data`: Request data (for create/update)
|
||||
* `Result`: Operation result (for after hooks)
|
||||
* `Writer`: Response writer (allows hooks to modify response)
|
||||
|
||||
### Cursor Pagination
|
||||
|
||||
RestHeadSpec supports efficient cursor-based pagination for large datasets:
|
||||
|
||||
```http
|
||||
```HTTP
|
||||
GET /public/posts HTTP/1.1
|
||||
X-Sort: -created_at,+id
|
||||
X-Limit: 50
|
||||
@@ -295,20 +304,22 @@ X-Cursor-Forward: <cursor_token>
|
||||
```
|
||||
|
||||
**How it works**:
|
||||
|
||||
1. First request returns results + cursor token in response
|
||||
2. Subsequent requests use `X-Cursor-Forward` or `X-Cursor-Backward`
|
||||
3. Cursor maintains consistent ordering even with data changes
|
||||
4. Supports complex multi-column sorting
|
||||
|
||||
**Benefits over offset pagination**:
|
||||
- Consistent results when data changes
|
||||
- Better performance for large offsets
|
||||
- Prevents "skipped" or duplicate records
|
||||
- Works with complex sort expressions
|
||||
|
||||
* Consistent results when data changes
|
||||
* Better performance for large offsets
|
||||
* Prevents "skipped" or duplicate records
|
||||
* Works with complex sort expressions
|
||||
|
||||
**Example with hooks**:
|
||||
|
||||
```go
|
||||
```Go
|
||||
// Enable cursor pagination in a hook
|
||||
handler.Hooks.Register(restheadspec.BeforeRead, func(ctx *restheadspec.HookContext) error {
|
||||
// For large tables, enforce cursor pagination
|
||||
@@ -324,7 +335,8 @@ handler.Hooks.Register(restheadspec.BeforeRead, func(ctx *restheadspec.HookConte
|
||||
RestHeadSpec supports multiple response formats:
|
||||
|
||||
**1. Simple Format** (`X-SimpleApi: true`):
|
||||
```json
|
||||
|
||||
```JSON
|
||||
[
|
||||
{ "id": 1, "name": "John" },
|
||||
{ "id": 2, "name": "Jane" }
|
||||
@@ -332,7 +344,8 @@ RestHeadSpec supports multiple response formats:
|
||||
```
|
||||
|
||||
**2. Detail Format** (`X-DetailApi: true`, default):
|
||||
```json
|
||||
|
||||
```JSON
|
||||
{
|
||||
"success": true,
|
||||
"data": [...],
|
||||
@@ -346,7 +359,8 @@ RestHeadSpec supports multiple response formats:
|
||||
```
|
||||
|
||||
**3. Syncfusion Format** (`X-Syncfusion: true`):
|
||||
```json
|
||||
|
||||
```JSON
|
||||
{
|
||||
"result": [...],
|
||||
"count": 100
|
||||
@@ -358,10 +372,12 @@ RestHeadSpec supports multiple response formats:
|
||||
By default, RestHeadSpec automatically converts single-element arrays into objects for cleaner API responses. This provides a better developer experience when fetching individual records.
|
||||
|
||||
**Default behavior (enabled)**:
|
||||
```http
|
||||
|
||||
```HTTP
|
||||
GET /public/users/123
|
||||
```
|
||||
```json
|
||||
|
||||
```JSON
|
||||
{
|
||||
"success": true,
|
||||
"data": { "id": 123, "name": "John", "email": "john@example.com" }
|
||||
@@ -369,7 +385,8 @@ GET /public/users/123
|
||||
```
|
||||
|
||||
Instead of:
|
||||
```json
|
||||
|
||||
```JSON
|
||||
{
|
||||
"success": true,
|
||||
"data": [{ "id": 123, "name": "John", "email": "john@example.com" }]
|
||||
@@ -377,11 +394,13 @@ Instead of:
|
||||
```
|
||||
|
||||
**To disable** (force arrays for consistency):
|
||||
```http
|
||||
|
||||
```HTTP
|
||||
GET /public/users/123
|
||||
X-Single-Record-As-Object: false
|
||||
```
|
||||
```json
|
||||
|
||||
```JSON
|
||||
{
|
||||
"success": true,
|
||||
"data": [{ "id": 123, "name": "John", "email": "john@example.com" }]
|
||||
@@ -389,23 +408,26 @@ X-Single-Record-As-Object: false
|
||||
```
|
||||
|
||||
**How it works**:
|
||||
- When a query returns exactly **one record**, it's returned as an object
|
||||
- When a query returns **multiple records**, they're returned as an array
|
||||
- Set `X-Single-Record-As-Object: false` to always receive arrays
|
||||
- Works with all response formats (simple, detail, syncfusion)
|
||||
- Applies to both read operations and create/update returning clauses
|
||||
|
||||
* When a query returns exactly **one record**, it's returned as an object
|
||||
* When a query returns **multiple records**, they're returned as an array
|
||||
* Set `X-Single-Record-As-Object: false` to always receive arrays
|
||||
* Works with all response formats (simple, detail, syncfusion)
|
||||
* Applies to both read operations and create/update returning clauses
|
||||
|
||||
**Benefits**:
|
||||
- Cleaner API responses for single-record queries
|
||||
- No need to unwrap single-element arrays on the client side
|
||||
- Better TypeScript/type inference support
|
||||
- Consistent with common REST API patterns
|
||||
- Backward compatible via header opt-out
|
||||
|
||||
* Cleaner API responses for single-record queries
|
||||
* No need to unwrap single-element arrays on the client side
|
||||
* Better TypeScript/type inference support
|
||||
* Consistent with common REST API patterns
|
||||
* Backward compatible via header opt-out
|
||||
|
||||
## Example Usage
|
||||
|
||||
### Reading Data with Related Entities
|
||||
```json
|
||||
|
||||
```JSON
|
||||
POST /core/users
|
||||
{
|
||||
"operation": "read",
|
||||
@@ -449,7 +471,7 @@ ResolveSpec now supports automatic handling of nested object graphs with intelli
|
||||
|
||||
#### Creating Nested Objects
|
||||
|
||||
```json
|
||||
```JSON
|
||||
POST /core/users
|
||||
{
|
||||
"operation": "create",
|
||||
@@ -482,7 +504,7 @@ POST /core/users
|
||||
|
||||
Control individual operations for each nested record using the special `_request` field:
|
||||
|
||||
```json
|
||||
```JSON
|
||||
POST /core/users/123
|
||||
{
|
||||
"operation": "update",
|
||||
@@ -508,11 +530,12 @@ POST /core/users/123
|
||||
}
|
||||
```
|
||||
|
||||
**Supported `_request` values**:
|
||||
- `insert` - Create a new related record
|
||||
- `update` - Update an existing related record
|
||||
- `delete` - Delete a related record
|
||||
- `upsert` - Create if doesn't exist, update if exists
|
||||
**Supported** **`_request`** **values**:
|
||||
|
||||
* `insert` - Create a new related record
|
||||
* `update` - Update an existing related record
|
||||
* `delete` - Delete a related record
|
||||
* `upsert` - Create if doesn't exist, update if exists
|
||||
|
||||
#### How It Works
|
||||
|
||||
@@ -524,14 +547,14 @@ POST /core/users/123
|
||||
|
||||
#### Benefits
|
||||
|
||||
- Reduce API round trips for complex object graphs
|
||||
- Maintain referential integrity automatically
|
||||
- Simplify client-side code
|
||||
- Atomic operations with automatic rollback on errors
|
||||
* Reduce API round trips for complex object graphs
|
||||
* Maintain referential integrity automatically
|
||||
* Simplify client-side code
|
||||
* Atomic operations with automatic rollback on errors
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
```Shell
|
||||
go get github.com/bitechdev/ResolveSpec
|
||||
```
|
||||
|
||||
@@ -541,7 +564,7 @@ go get github.com/bitechdev/ResolveSpec
|
||||
|
||||
ResolveSpec uses JSON request bodies to specify query options:
|
||||
|
||||
```go
|
||||
```Go
|
||||
import "github.com/bitechdev/ResolveSpec/pkg/resolvespec"
|
||||
|
||||
// Create handler
|
||||
@@ -568,7 +591,7 @@ resolvespec.SetupRoutes(router, handler)
|
||||
|
||||
RestHeadSpec uses HTTP headers for query options instead of request body:
|
||||
|
||||
```go
|
||||
```Go
|
||||
import "github.com/bitechdev/ResolveSpec/pkg/restheadspec"
|
||||
|
||||
// Create handler with GORM
|
||||
@@ -597,7 +620,7 @@ See [RestHeadSpec: Header-Based API](#restheadspec-header-based-api-1) for compl
|
||||
|
||||
Your existing code continues to work without any changes:
|
||||
|
||||
```go
|
||||
```Go
|
||||
import "github.com/bitechdev/ResolveSpec/pkg/resolvespec"
|
||||
|
||||
// This still works exactly as before
|
||||
@@ -615,7 +638,7 @@ ResolveSpec v2.0 introduces a new database and router abstraction layer while ma
|
||||
|
||||
To update your imports:
|
||||
|
||||
```bash
|
||||
```Shell
|
||||
# Update go.mod
|
||||
go mod edit -replace github.com/Warky-Devs/ResolveSpec=github.com/bitechdev/ResolveSpec@latest
|
||||
go mod tidy
|
||||
@@ -627,7 +650,7 @@ go mod tidy
|
||||
|
||||
Alternatively, use find and replace in your project:
|
||||
|
||||
```bash
|
||||
```Shell
|
||||
find . -type f -name "*.go" -exec sed -i 's|github.com/Warky-Devs/ResolveSpec|github.com/bitechdev/ResolveSpec|g' {} +
|
||||
go mod tidy
|
||||
```
|
||||
@@ -642,7 +665,7 @@ go mod tidy
|
||||
|
||||
### Detailed Migration Guide
|
||||
|
||||
For detailed migration instructions, examples, and best practices, see [MIGRATION_GUIDE.md](MIGRATION_GUIDE.md).
|
||||
For detailed migration instructions, examples, and best practices, see [MIGRATION\_GUIDE.md](MIGRATION_GUIDE.md).
|
||||
|
||||
## Architecture
|
||||
|
||||
@@ -684,22 +707,23 @@ Your Application Code
|
||||
|
||||
### Supported Database Layers
|
||||
|
||||
- **GORM** (default, fully supported)
|
||||
- **Bun** (ready to use, included in dependencies)
|
||||
- **Custom ORMs** (implement the `Database` interface)
|
||||
* **GORM** (default, fully supported)
|
||||
* **Bun** (ready to use, included in dependencies)
|
||||
* **Custom ORMs** (implement the `Database` interface)
|
||||
|
||||
### Supported Routers
|
||||
|
||||
- **Gorilla Mux** (built-in support with `SetupRoutes()`)
|
||||
- **BunRouter** (built-in support with `SetupBunRouterWithResolveSpec()`)
|
||||
- **Gin** (manual integration, see examples above)
|
||||
- **Echo** (manual integration, see examples above)
|
||||
- **Custom Routers** (implement request/response adapters)
|
||||
* **Gorilla Mux** (built-in support with `SetupRoutes()`)
|
||||
* **BunRouter** (built-in support with `SetupBunRouterWithResolveSpec()`)
|
||||
* **Gin** (manual integration, see examples above)
|
||||
* **Echo** (manual integration, see examples above)
|
||||
* **Custom Routers** (implement request/response adapters)
|
||||
|
||||
### Option 2: New Database-Agnostic API
|
||||
|
||||
#### With GORM (Recommended Migration Path)
|
||||
```go
|
||||
|
||||
```Go
|
||||
import "github.com/bitechdev/ResolveSpec/pkg/resolvespec"
|
||||
|
||||
// Create database adapter
|
||||
@@ -715,7 +739,8 @@ handler := resolvespec.NewHandler(dbAdapter, registry)
|
||||
```
|
||||
|
||||
#### With Bun ORM
|
||||
```go
|
||||
|
||||
```Go
|
||||
import "github.com/bitechdev/ResolveSpec/pkg/resolvespec"
|
||||
import "github.com/uptrace/bun"
|
||||
|
||||
@@ -730,7 +755,8 @@ handler := resolvespec.NewHandler(dbAdapter, registry)
|
||||
### Router Integration
|
||||
|
||||
#### Gorilla Mux (Built-in Support)
|
||||
```go
|
||||
|
||||
```Go
|
||||
import "github.com/gorilla/mux"
|
||||
|
||||
// Register models first
|
||||
@@ -746,7 +772,8 @@ resolvespec.SetupMuxRoutes(router, handler, nil)
|
||||
```
|
||||
|
||||
#### Gin (Custom Integration)
|
||||
```go
|
||||
|
||||
```Go
|
||||
import "github.com/gin-gonic/gin"
|
||||
|
||||
func setupGin(handler *resolvespec.Handler) *gin.Engine {
|
||||
@@ -769,7 +796,8 @@ func setupGin(handler *resolvespec.Handler) *gin.Engine {
|
||||
```
|
||||
|
||||
#### Echo (Custom Integration)
|
||||
```go
|
||||
|
||||
```Go
|
||||
import "github.com/labstack/echo/v4"
|
||||
|
||||
func setupEcho(handler *resolvespec.Handler) *echo.Echo {
|
||||
@@ -792,7 +820,8 @@ func setupEcho(handler *resolvespec.Handler) *echo.Echo {
|
||||
```
|
||||
|
||||
#### BunRouter (Built-in Support)
|
||||
```go
|
||||
|
||||
```Go
|
||||
import "github.com/uptrace/bunrouter"
|
||||
|
||||
// Simple setup with built-in function
|
||||
@@ -837,7 +866,8 @@ func setupFullUptrace(bunDB *bun.DB) *bunrouter.Router {
|
||||
## Configuration
|
||||
|
||||
### Model Registration
|
||||
```go
|
||||
|
||||
```Go
|
||||
type User struct {
|
||||
ID uint `json:"id" gorm:"primaryKey"`
|
||||
Name string `json:"name"`
|
||||
@@ -851,20 +881,24 @@ handler.RegisterModel("core", "users", &User{})
|
||||
## Features in Detail
|
||||
|
||||
### Filtering
|
||||
|
||||
Supported operators:
|
||||
- eq: Equal
|
||||
- neq: Not Equal
|
||||
- gt: Greater Than
|
||||
- gte: Greater Than or Equal
|
||||
- lt: Less Than
|
||||
- lte: Less Than or Equal
|
||||
- like: LIKE pattern matching
|
||||
- ilike: Case-insensitive LIKE
|
||||
- in: IN clause
|
||||
|
||||
* eq: Equal
|
||||
* neq: Not Equal
|
||||
* gt: Greater Than
|
||||
* gte: Greater Than or Equal
|
||||
* lt: Less Than
|
||||
* lte: Less Than or Equal
|
||||
* like: LIKE pattern matching
|
||||
* ilike: Case-insensitive LIKE
|
||||
* in: IN clause
|
||||
|
||||
### Sorting
|
||||
|
||||
Support for multiple sort criteria with direction:
|
||||
```json
|
||||
|
||||
```JSON
|
||||
"sort": [
|
||||
{
|
||||
"column": "created_at",
|
||||
@@ -878,8 +912,10 @@ Support for multiple sort criteria with direction:
|
||||
```
|
||||
|
||||
### Computed Columns
|
||||
|
||||
Define virtual columns using SQL expressions:
|
||||
```json
|
||||
|
||||
```JSON
|
||||
"computedColumns": [
|
||||
{
|
||||
"name": "full_name",
|
||||
@@ -892,7 +928,7 @@ Define virtual columns using SQL expressions:
|
||||
|
||||
### With New Architecture (Mockable)
|
||||
|
||||
```go
|
||||
```Go
|
||||
import "github.com/stretchr/testify/mock"
|
||||
|
||||
// Create mock database
|
||||
@@ -927,14 +963,14 @@ ResolveSpec uses GitHub Actions for automated testing and quality checks. The CI
|
||||
|
||||
The project includes automated workflows that:
|
||||
|
||||
- **Test**: Run all tests with race detection and code coverage
|
||||
- **Lint**: Check code quality with golangci-lint
|
||||
- **Build**: Verify the project builds successfully
|
||||
- **Multi-version**: Test against multiple Go versions (1.23.x, 1.24.x)
|
||||
* **Test**: Run all tests with race detection and code coverage
|
||||
* **Lint**: Check code quality with golangci-lint
|
||||
* **Build**: Verify the project builds successfully
|
||||
* **Multi-version**: Test against multiple Go versions (1.23.x, 1.24.x)
|
||||
|
||||
### Running Tests Locally
|
||||
|
||||
```bash
|
||||
```Shell
|
||||
# Run all tests
|
||||
go test -v ./...
|
||||
|
||||
@@ -952,13 +988,13 @@ golangci-lint run
|
||||
|
||||
The project includes comprehensive test coverage:
|
||||
|
||||
- **Unit Tests**: Individual component testing
|
||||
- **Integration Tests**: End-to-end API testing
|
||||
- **CRUD Tests**: Standalone tests for both ResolveSpec and RestHeadSpec APIs
|
||||
* **Unit Tests**: Individual component testing
|
||||
* **Integration Tests**: End-to-end API testing
|
||||
* **CRUD Tests**: Standalone tests for both ResolveSpec and RestHeadSpec APIs
|
||||
|
||||
To run only the CRUD standalone tests:
|
||||
|
||||
```bash
|
||||
```Shell
|
||||
go test -v ./tests -run TestCRUDStandalone
|
||||
```
|
||||
|
||||
@@ -970,18 +1006,18 @@ Check the [Actions tab](../../actions) on GitHub to see the status of recent CI
|
||||
|
||||
Add this badge to display CI status in your fork:
|
||||
|
||||
```markdown
|
||||
```Markdown
|
||||

|
||||
```
|
||||
|
||||
## Security Considerations
|
||||
|
||||
- Implement proper authentication and authorization
|
||||
- Validate all input parameters
|
||||
- Use prepared statements (handled by GORM/Bun/your ORM)
|
||||
- Implement rate limiting
|
||||
- Control access at schema/entity level
|
||||
- **New**: Database abstraction layer provides additional security through interface boundaries
|
||||
* Implement proper authentication and authorization
|
||||
* Validate all input parameters
|
||||
* Use prepared statements (handled by GORM/Bun/your ORM)
|
||||
* Implement rate limiting
|
||||
* Control access at schema/entity level
|
||||
* **New**: Database abstraction layer provides additional security through interface boundaries
|
||||
|
||||
## Contributing
|
||||
|
||||
@@ -993,94 +1029,106 @@ Add this badge to display CI status in your fork:
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
||||
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
||||
|
||||
## What's New
|
||||
|
||||
### v3.0 (Latest - December 2025)
|
||||
|
||||
**Explicit Route Registration (🆕)**:
|
||||
- **Breaking Change**: Routes are now created explicitly for each registered model
|
||||
- **Better Control**: Customize routes per model with more flexibility
|
||||
- **Registration Order**: Models must be registered BEFORE calling SetupMuxRoutes/SetupBunRouterRoutes
|
||||
- **Benefits**: More flexible routing, easier to add custom routes per model, better performance
|
||||
|
||||
* **Breaking Change**: Routes are now created explicitly for each registered model
|
||||
* **Better Control**: Customize routes per model with more flexibility
|
||||
* **Registration Order**: Models must be registered BEFORE calling SetupMuxRoutes/SetupBunRouterRoutes
|
||||
* **Benefits**: More flexible routing, easier to add custom routes per model, better performance
|
||||
|
||||
**OPTIONS Method & CORS Support (🆕)**:
|
||||
- **OPTIONS Endpoint**: Full OPTIONS method support for CORS preflight requests
|
||||
- **Metadata Response**: OPTIONS returns model metadata (same as GET /metadata)
|
||||
- **CORS Headers**: Comprehensive CORS headers on all responses
|
||||
- **Header Support**: All HeadSpec custom headers (`X-Select-Fields`, `X-FieldFilter-*`, etc.) allowed
|
||||
- **No Auth on OPTIONS**: CORS preflight requests don't require authentication
|
||||
- **Configurable**: Customize CORS settings via `common.CORSConfig`
|
||||
|
||||
* **OPTIONS Endpoint**: Full OPTIONS method support for CORS preflight requests
|
||||
* **Metadata Response**: OPTIONS returns model metadata (same as GET /metadata)
|
||||
* **CORS Headers**: Comprehensive CORS headers on all responses
|
||||
* **Header Support**: All HeadSpec custom headers (`X-Select-Fields`, `X-FieldFilter-*`, etc.) allowed
|
||||
* **No Auth on OPTIONS**: CORS preflight requests don't require authentication
|
||||
* **Configurable**: Customize CORS settings via `common.CORSConfig`
|
||||
|
||||
**Migration Notes**:
|
||||
- Update your code to register models BEFORE calling SetupMuxRoutes/SetupBunRouterRoutes
|
||||
- Routes like `/public/users` are now created per registered model instead of using dynamic `/{schema}/{entity}` pattern
|
||||
- This is a **breaking change** but provides better control and flexibility
|
||||
|
||||
* Update your code to register models BEFORE calling SetupMuxRoutes/SetupBunRouterRoutes
|
||||
* Routes like `/public/users` are now created per registered model instead of using dynamic `/{schema}/{entity}` pattern
|
||||
* This is a **breaking change** but provides better control and flexibility
|
||||
|
||||
### v2.1
|
||||
|
||||
**Recursive CRUD Handler (🆕 Nov 11, 2025)**:
|
||||
- **Nested Object Graphs**: Automatically handle complex object hierarchies with parent-child relationships
|
||||
- **Foreign Key Resolution**: Automatic propagation of parent IDs to child records
|
||||
- **Per-Record Operations**: Control create/update/delete operations per record via `_request` field
|
||||
- **Transaction Safety**: All nested operations execute atomically within database transactions
|
||||
- **Relationship Detection**: Automatic detection of belongsTo, hasMany, hasOne, and many2many relationships
|
||||
- **Deep Nesting Support**: Handle relationships at any depth level
|
||||
- **Mixed Operations**: Combine insert, update, and delete operations in a single request
|
||||
|
||||
* **Nested Object Graphs**: Automatically handle complex object hierarchies with parent-child relationships
|
||||
* **Foreign Key Resolution**: Automatic propagation of parent IDs to child records
|
||||
* **Per-Record Operations**: Control create/update/delete operations per record via `_request` field
|
||||
* **Transaction Safety**: All nested operations execute atomically within database transactions
|
||||
* **Relationship Detection**: Automatic detection of belongsTo, hasMany, hasOne, and many2many relationships
|
||||
* **Deep Nesting Support**: Handle relationships at any depth level
|
||||
* **Mixed Operations**: Combine insert, update, and delete operations in a single request
|
||||
|
||||
**Primary Key Improvements (Nov 11, 2025)**:
|
||||
- **GetPrimaryKeyName**: Enhanced primary key detection for better preload and ID field handling
|
||||
- **Better GORM/Bun Support**: Improved compatibility with both ORMs for primary key operations
|
||||
- **Computed Column Support**: Fixed computed columns functionality across handlers
|
||||
|
||||
* **GetPrimaryKeyName**: Enhanced primary key detection for better preload and ID field handling
|
||||
* **Better GORM/Bun Support**: Improved compatibility with both ORMs for primary key operations
|
||||
* **Computed Column Support**: Fixed computed columns functionality across handlers
|
||||
|
||||
**Database Adapter Enhancements (Nov 11, 2025)**:
|
||||
- **Bun ORM Relations**: Using Scan model method for better has-many and many-to-many relationship handling
|
||||
- **Model Method Support**: Enhanced query building with proper model registration
|
||||
- **Improved Type Safety**: Better handling of relationship queries with type-aware scanning
|
||||
|
||||
* **Bun ORM Relations**: Using Scan model method for better has-many and many-to-many relationship handling
|
||||
* **Model Method Support**: Enhanced query building with proper model registration
|
||||
* **Improved Type Safety**: Better handling of relationship queries with type-aware scanning
|
||||
|
||||
**RestHeadSpec - Header-Based REST API**:
|
||||
- **Header-Based Querying**: All query options via HTTP headers instead of request body
|
||||
- **Lifecycle Hooks**: Before/after hooks for create, read, update, delete operations
|
||||
- **Cursor Pagination**: Efficient cursor-based pagination with complex sorting
|
||||
- **Advanced Filtering**: Field filters, search operators, AND/OR logic
|
||||
- **Multiple Response Formats**: Simple, detailed, and Syncfusion-compatible responses
|
||||
- **Single Record as Object**: Automatically return single-element arrays as objects (default, toggleable via header)
|
||||
- **Base64 Support**: Base64-encoded header values for complex queries
|
||||
- **Type-Aware Filtering**: Automatic type detection and conversion for filters
|
||||
|
||||
* **Header-Based Querying**: All query options via HTTP headers instead of request body
|
||||
* **Lifecycle Hooks**: Before/after hooks for create, read, update, delete operations
|
||||
* **Cursor Pagination**: Efficient cursor-based pagination with complex sorting
|
||||
* **Advanced Filtering**: Field filters, search operators, AND/OR logic
|
||||
* **Multiple Response Formats**: Simple, detailed, and Syncfusion-compatible responses
|
||||
* **Single Record as Object**: Automatically return single-element arrays as objects (default, toggleable via header)
|
||||
* **Base64 Support**: Base64-encoded header values for complex queries
|
||||
* **Type-Aware Filtering**: Automatic type detection and conversion for filters
|
||||
|
||||
**Core Improvements**:
|
||||
- Better model registry with schema.table format support
|
||||
- Enhanced validation and error handling
|
||||
- Improved reflection safety
|
||||
- Fixed COUNT query issues with table aliasing
|
||||
- Better pointer handling throughout the codebase
|
||||
- **Comprehensive Test Coverage**: Added standalone CRUD tests for both ResolveSpec and RestHeadSpec
|
||||
|
||||
* Better model registry with schema.table format support
|
||||
* Enhanced validation and error handling
|
||||
* Improved reflection safety
|
||||
* Fixed COUNT query issues with table aliasing
|
||||
* Better pointer handling throughout the codebase
|
||||
* **Comprehensive Test Coverage**: Added standalone CRUD tests for both ResolveSpec and RestHeadSpec
|
||||
|
||||
### v2.0
|
||||
|
||||
**Breaking Changes**:
|
||||
- **None!** Full backward compatibility maintained
|
||||
|
||||
* **None!** Full backward compatibility maintained
|
||||
|
||||
**New Features**:
|
||||
- **Database Abstraction**: Support for GORM, Bun, and custom ORMs
|
||||
- **Router Flexibility**: Works with any HTTP router through adapters
|
||||
- **BunRouter Integration**: Built-in support for uptrace/bunrouter
|
||||
- **Better Architecture**: Clean separation of concerns with interfaces
|
||||
- **Enhanced Testing**: Mockable interfaces for comprehensive testing
|
||||
- **Migration Guide**: Step-by-step migration instructions
|
||||
|
||||
* **Database Abstraction**: Support for GORM, Bun, and custom ORMs
|
||||
* **Router Flexibility**: Works with any HTTP router through adapters
|
||||
* **BunRouter Integration**: Built-in support for uptrace/bunrouter
|
||||
* **Better Architecture**: Clean separation of concerns with interfaces
|
||||
* **Enhanced Testing**: Mockable interfaces for comprehensive testing
|
||||
* **Migration Guide**: Step-by-step migration instructions
|
||||
|
||||
**Performance Improvements**:
|
||||
- More efficient query building through interface design
|
||||
- Reduced coupling between components
|
||||
- Better memory management with interface boundaries
|
||||
|
||||
* More efficient query building through interface design
|
||||
* Reduced coupling between components
|
||||
* Better memory management with interface boundaries
|
||||
|
||||
## Acknowledgments
|
||||
|
||||
- Inspired by REST, OData, and GraphQL's flexibility
|
||||
- **Header-based approach**: Inspired by REST best practices and clean API design
|
||||
- **Database Support**: [GORM](https://gorm.io) and [Bun](https://bun.uptrace.dev/)
|
||||
- **Router Support**: Gorilla Mux (built-in), BunRouter, Gin, Echo, and others through adapters
|
||||
- Slogan generated using DALL-E
|
||||
- AI used for documentation checking and correction
|
||||
- Community feedback and contributions that made v2.0 and v2.1 possible
|
||||
* Inspired by REST, OData, and GraphQL's flexibility
|
||||
* **Header-based approach**: Inspired by REST best practices and clean API design
|
||||
* **Database Support**: [GORM](https://gorm.io) and [Bun](https://bun.uptrace.dev/)
|
||||
* **Router Support**: Gorilla Mux (built-in), BunRouter, Gin, Echo, and others through adapters
|
||||
* Slogan generated using DALL-E
|
||||
* AI used for documentation checking and correction
|
||||
* Community feedback and contributions that made v2.0 and v2.1 possible
|
||||
|
||||
|
||||
Reference in New Issue
Block a user