# 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