Files
whatshooked/tooldoc/RESOLVESPEC.md
Hein f9773bd07f
Some checks failed
CI / Test (1.23) (push) Failing after -22m46s
CI / Test (1.22) (push) Failing after -22m32s
CI / Build (push) Failing after -23m30s
CI / Lint (push) Failing after -23m12s
refactor(API): Relspect integration
2026-02-05 13:39:43 +02:00

360 lines
7.9 KiB
Markdown

# ResolveSpec Integration Guide
## Overview
ResolveSpec is a flexible REST API framework that provides GraphQL-like capabilities while maintaining REST simplicity. It offers two approaches:
1. **ResolveSpec** - Body-based API with JSON request options
2. **RestHeadSpec** - Header-based API where query options are passed via HTTP headers
For WhatsHooked, we'll use both approaches to provide maximum flexibility.
## Installation
```bash
go get github.com/bitechdev/ResolveSpec
```
## Core Concepts
### Models
Models are Go structs that represent database tables. Use GORM tags for database mapping.
```go
type User struct {
ID string `gorm:"primaryKey" json:"id"`
Name string `gorm:"not null" json:"name"`
Email string `gorm:"uniqueIndex;not null" json:"email"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
```
### Registry
The registry maps schema.table names to Go models.
```go
handler := resolvespec.NewHandlerWithGORM(db)
handler.Registry.RegisterModel("public.users", &User{})
handler.Registry.RegisterModel("public.hooks", &Hook{})
```
### Routing
ResolveSpec generates routes automatically for registered models:
- `/public/users` - Collection endpoints
- `/public/users/:id` - Individual resource endpoints
## ResolveSpec (Body-Based)
Request format:
```json
POST /public/users
{
"operation": "read|create|update|delete",
"data": {
// For create/update operations
},
"options": {
"columns": ["id", "name", "email"],
"filters": [
{"column": "status", "operator": "eq", "value": "active"}
],
"preload": ["hooks:id,url,events"],
"sort": ["-created_at", "+name"],
"limit": 50,
"offset": 0
}
}
```
### Setup with Gorilla Mux
```go
import (
"github.com/gorilla/mux"
"github.com/bitechdev/ResolveSpec/pkg/resolvespec"
)
func SetupResolveSpec(db *gorm.DB) *mux.Router {
handler := resolvespec.NewHandlerWithGORM(db)
// Register models
handler.Registry.RegisterModel("public.users", &User{})
handler.Registry.RegisterModel("public.hooks", &Hook{})
// Setup routes
router := mux.NewRouter()
resolvespec.SetupMuxRoutes(router, handler, nil)
return router
}
```
## RestHeadSpec (Header-Based)
Request format:
```http
GET /public/users HTTP/1.1
X-Select-Fields: id,name,email
X-FieldFilter-Status: active
X-Preload: hooks:id,url,events
X-Sort: -created_at,+name
X-Limit: 50
X-Offset: 0
X-DetailApi: true
```
### Setup with Gorilla Mux
```go
import (
"github.com/gorilla/mux"
"github.com/bitechdev/ResolveSpec/pkg/restheadspec"
)
func SetupRestHeadSpec(db *gorm.DB) *mux.Router {
handler := restheadspec.NewHandlerWithGORM(db)
// Register models
handler.Registry.RegisterModel("public.users", &User{})
handler.Registry.RegisterModel("public.hooks", &Hook{})
// Setup routes
router := mux.NewRouter()
restheadspec.SetupMuxRoutes(router, handler, nil)
return router
}
```
## Lifecycle Hooks (RestHeadSpec)
Add hooks for authentication, validation, and audit logging:
```go
handler.OnBeforeRead(func(ctx context.Context, req *restheadspec.Request) error {
// Check permissions
userID := ctx.Value("user_id").(string)
if !canRead(userID, req.Schema, req.Entity) {
return fmt.Errorf("unauthorized")
}
return nil
})
handler.OnAfterCreate(func(ctx context.Context, req *restheadspec.Request, result interface{}) error {
// Audit log
log.Info().
Str("user_id", ctx.Value("user_id").(string)).
Str("entity", req.Entity).
Msg("Created record")
return nil
})
```
## Authentication Integration
```go
// Middleware to extract user from JWT
func AuthMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
token := r.Header.Get("Authorization")
user, err := ValidateToken(token)
if err != nil {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
ctx := context.WithValue(r.Context(), "user_id", user.ID)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
// Apply to routes
router.Use(AuthMiddleware)
```
## Filtering
### Field Filters (RestHeadSpec)
```http
X-FieldFilter-Status: active
X-FieldFilter-Age: 18
```
### Search Operators (RestHeadSpec)
```http
X-SearchOp-Gte-Age: 18
X-SearchOp-Like-Name: john
```
### Body Filters (ResolveSpec)
```json
{
"options": {
"filters": [
{"column": "status", "operator": "eq", "value": "active"},
{"column": "age", "operator": "gte", "value": 18},
{"column": "name", "operator": "like", "value": "john%"}
]
}
}
```
## Pagination
### Offset-Based
```http
X-Limit: 50
X-Offset: 100
```
### Cursor-Based (RestHeadSpec)
```http
X-Cursor: eyJpZCI6IjEyMyIsImNyZWF0ZWRfYXQiOiIyMDI0LTAxLTAxIn0=
X-Limit: 50
```
## Preloading Relationships
Load related entities with custom columns:
```http
X-Preload: hooks:id,url,events,posts:id,title
```
```json
{
"options": {
"preload": ["hooks:id,url,events", "posts:id,title"]
}
}
```
## Sorting
```http
X-Sort: -created_at,+name
```
Prefix with `-` for descending, `+` for ascending.
## Response Formats (RestHeadSpec)
### Simple Format (default)
```http
X-DetailApi: false
```
Returns: `[{...}, {...}]`
### Detailed Format
```http
X-DetailApi: true
```
Returns:
```json
{
"data": [{...}, {...}],
"meta": {
"total": 100,
"limit": 50,
"offset": 0,
"cursor": "..."
}
}
```
## CORS Configuration
```go
corsConfig := &common.CORSConfig{
AllowedOrigins: []string{"*"},
AllowedMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
AllowedHeaders: []string{"*"},
ExposedHeaders: []string{"X-Total-Count", "X-Cursor"},
}
restheadspec.SetupMuxRoutes(router, handler, corsConfig)
```
## Error Handling
ResolveSpec returns standard HTTP error codes:
- 200: Success
- 400: Bad Request
- 401: Unauthorized
- 404: Not Found
- 500: Internal Server Error
Error response format:
```json
{
"error": "error message",
"details": "additional context"
}
```
## Best Practices
1. **Register models before routes**: Always register all models before calling SetupMuxRoutes
2. **Use lifecycle hooks**: Implement authentication and validation in hooks
3. **Schema naming**: Use `schema.table` format consistently
4. **Transactions**: Use database transactions for multi-record operations
5. **Validation**: Validate input in OnBeforeCreate/OnBeforeUpdate hooks
6. **Audit logging**: Use OnAfter* hooks for audit trails
7. **Performance**: Use preloading instead of N+1 queries
8. **Security**: Implement row-level security in hooks
9. **Rate limiting**: Add rate limiting middleware
10. **Monitoring**: Log all operations for monitoring
## Common Patterns
### User Filtering (Multi-tenancy)
```go
handler.OnBeforeRead(func(ctx context.Context, req *restheadspec.Request) error {
userID := ctx.Value("user_id").(string)
// Add user_id filter
req.Options.Filters = append(req.Options.Filters, Filter{
Column: "user_id",
Operator: "eq",
Value: userID,
})
return nil
})
```
### Soft Deletes
```go
handler.OnBeforeDelete(func(ctx context.Context, req *restheadspec.Request) error {
// Convert to update with deleted_at
req.Operation = "update"
req.Data = map[string]interface{}{
"deleted_at": time.Now(),
}
return nil
})
```
### Validation
```go
handler.OnBeforeCreate(func(ctx context.Context, req *restheadspec.Request) error {
user := req.Data.(*User)
if user.Email == "" {
return fmt.Errorf("email is required")
}
if !isValidEmail(user.Email) {
return fmt.Errorf("invalid email format")
}
return nil
})
```
## References
- Official Docs: https://github.com/bitechdev/ResolveSpec
- ResolveSpec README: /pkg/resolvespec/README.md
- RestHeadSpec README: /pkg/restheadspec/README.md