SkillAgentSearch skills...

api

Self-hostable personal AI assistant

Install / Use

npx skills add theimaginaryfoundation/what-iff

Installs into whichever agent you are using.

About this skill
📐

Cursor Rules

Cursor IDE rules (v2)

Quality Score

73/100

Supported Platforms

Cursor

Our assessment of api

api scores 73/100 on our quality scale, 3757th of 4,632 Development & Engineering skills we index.

Its Cursor Rules is 20 KB long, well organised into 31 sections with 20 code examples: a thorough specification that gives an agent plenty to work with.

It has no GitHub stars yet, so there is no community track record; judge it on its content.

Substance
30/30
Structure
20/20
Description
8/15
Adoption
0/20
Freshness
15/15

Maintenance, license and trust

  • The repository was last updated 9 days ago, so api is actively maintained.
  • Our last check on 2026-10-18 found the source still online.
  • No license is declared. By default that means all rights are reserved: you can read it, but reusing or redistributing it is not clearly permitted. Ask the author before building on it commercially.
  • Its trust signals score 80/100, with 2 cautions from licensing, adoption, age or documentation. These come from repository metadata, not a code audit — read the skill file before letting an agent act on it.

Safety scan

No issues found

Our scan of the whole file found no instruction hijacking, hidden characters, credential access, data exfiltration or destructive commands. An AI review of the same text found nothing harmful.

AI review by kimi-k2.7-code on 2026-09-24. Automated pattern scan on 2026-09-24. It catches known dangerous patterns, not every risk — read a skill before letting an agent act on it.

api compared with similar skills

All 4 of these similar skills score higher than api; compare them before choosing.

SkillScoreStarsUpdatedFormat
api (this skill)by theimaginaryfoundation7309d agoCursor Rules
taste-skillby Leonxlnx100108.6k2d agoCLAUDE.md
Agent-Reachby Panniantong100108.0k1d agoCLAUDE.md
headroomby headroomlabs-ai10078.7ktodayCLAUDE.md
ai-job-searchby ai-job-search-dev10069.0ktodayCLAUDE.md

Frequently asked questions

How do I install api?
Run npx skills add theimaginaryfoundation/what-iff. The install tabs above show the steps for each supported agent.
Which AI agents does api work with?
It is written for Cursor, as a Cursor Rules file. Other agents that read the same format can often use it too.
Is api safe to use?
Our scan of the whole file found no instruction hijacking, hidden characters, credential access, data exfiltration or destructive commands. An AI review of the same text found nothing harmful. It declares no license and scores 80/100 on trust signals. Skills are instructions an agent will follow, so read the file before installing it and do not approve commands you do not understand.
Is api still maintained?
The repository was last updated 9 days ago, so api is actively maintained.

description: globs: internal/auth/.go,internal/handlers/**/.go,internal/server/*.go alwaysApply: false

API Server Design and Structure

Overview

The API server is a RESTful service built with Go, using the Gorilla Mux router for routing and Ent for database operations. The server follows a modular design with clear separation of concerns and dependency injection patterns.

Server Initialization

The API server is initialized in cmd/api-server/main.go. It follows these steps:

  1. Load environment variables
  2. Initialize logger
  3. Connect to database
  4. Create and configure server
  5. Start server and handle graceful shutdown
func main() {
    // Load environment variables
    if err := godotenv.Load(envFile); err != nil {
        log.Printf("Warning: Error loading .env file: %v", err)
    }

    // Initialize logger
    logger, err := zap.NewProduction()
    if err != nil {
        log.Fatalf("Failed to initialize logger: %v", err)
    }
    defer logger.Sync()

    // Initialize database connection
    client, err := database.NewClient(logger)
    if err != nil {
        logger.Fatal("Failed to connect to database", zap.Error(err))
    }
    defer client.Close()

    // Create server config
    config := server.NewConfig()

    // Create and configure server
    srv := server.NewServer(config, logger, client)

    // Run server in a goroutine
    go func() {
        if err := srv.Start(); err != nil {
            logger.Fatal("Server failed", zap.Error(err))
        }
    }()

    // Handle graceful shutdown
    c := make(chan os.Signal, 1)
    signal.Notify(c, os.Interrupt, syscall.SIGTERM)
    sig := <-c
    
    ctx, cancel := context.WithTimeout(context.Background(), time.Second*15)
    defer cancel()
    
    if err := srv.Shutdown(ctx); err != nil {
        logger.Fatal("Server forced to shutdown", zap.Error(err))
    }
}

Server Configuration

Server configuration is managed through the Config struct in internal/server/config.go. It loads settings from environment variables with sensible defaults.

type Config struct {
    Host          string
    Port          string
    ReadTimeout   time.Duration
    WriteTimeout  time.Duration
    IdleTimeout   time.Duration
    RunMigrations bool
}

func NewConfig() *Config {
    port := os.Getenv("SERVER_PORT")
    if port == "" {
        port = "8080"
    }

    host := os.Getenv("SERVER_HOST")
    if host == "" {
        host = "localhost"
    }

    return &Config{
        Host:          host,
        Port:          port,
        ReadTimeout:   15 * time.Second,
        WriteTimeout:  15 * time.Second,
        IdleTimeout:   60 * time.Second,
        RunMigrations: false,
    }
}

Server Structure

The server is structured around a central Server struct in internal/server/server.go which encapsulates:

  • Configuration
  • Logger
  • Router
  • HTTP server
  • Database client
type Server struct {
    config *Config
    logger *zap.Logger
    router *mux.Router
    server *http.Server
    db     *ent.Client
}

func NewServer(config *Config, logger *zap.Logger, db *ent.Client) *Server {
    s := &Server{
        config: config,
        logger: logger,
        router: mux.NewRouter(),
        db:     db,
    }

    s.setupMiddleware()
    s.setupRoutes()

    s.server = &http.Server{
        Addr:         fmt.Sprintf("%s:%s", config.Host, config.Port),
        Handler:      s.router,
        WriteTimeout: config.WriteTimeout,
        ReadTimeout:  config.ReadTimeout,
        IdleTimeout:  config.IdleTimeout,
    }

    return s
}

Provider Pattern

We use the provider pattern to decouple handlers from data access implementations. Our approach has evolved to use composite provider interfaces for handlers that need access to multiple entity types.

Entity-Specific Provider Interfaces

Each entity has its own provider interface in the internal/providers/ directory:

// ContentBriefProvider defines the interface for content brief data operations
type ContentBriefProvider interface {
    CreateContentBrief(ctx context.Context, userID uuid.UUID, contentBrief models.ContentBrief) (*models.ContentBrief, error)
    ListContentBriefs(ctx context.Context, userID uuid.UUID, pageNum, pageSize int, filters models.ContentBriefFilters) (*models.PaginatedResponse, error)
    GetContentBrief(ctx context.Context, userID, id uuid.UUID) (*models.ContentBrief, error)
    UpdateContentBrief(ctx context.Context, userID uuid.UUID, contentBrief models.ContentBrief) (*models.ContentBrief, error)
    DeleteContentBrief(ctx context.Context, userID, id uuid.UUID) error
}

Composite Provider Interfaces

For handlers that need access to multiple entity types, we define composite interfaces that extend multiple entity provider interfaces:

// ProjectBriefQuestionIdeaJobProvider combines multiple provider interfaces
type ProjectBriefQuestionIdeaJobProvider interface {
    providers.ProjectProvider
    providers.ContentIdeaProvider
    providers.ContentBriefProvider
    providers.InterviewQuestionProvider
    providers.JobProvider
}

This approach simplifies handler constructors and avoids large parameter lists.

Generator Interfaces

For AI-powered content generation, we define handler-specific generator interfaces that include only the methods needed by each handler:

// ContentBriefQuestionGenerator defines the interface for generating content briefs and questions
type ContentBriefQuestionGenerator interface {
    GenerateContentBrief(ctx context.Context, contentIdea *models.ContentIdea, project *models.Project) (*models.CreateContentBrief, error)
    GenerateInterviewQuestions(ctx context.Context, brief *models.ContentBrief) ([]*models.InterviewQuestion, string, error)
}

This approach keeps the interfaces focused and ensures that handlers only depend on the generation capabilities they actually need.

Datastore Implementation

The datastore implements all provider interfaces:

// Datastore implements various provider interfaces
type Datastore struct {
    dbClient *ent.Client
    logger   *zap.Logger
}

func NewDatastore(dbClient *ent.Client, logger *zap.Logger) *Datastore {
    return &Datastore{
        dbClient: dbClient,
        logger:   logger,
    }
}

Handler Organization

Handlers are organized by domain/resource in the internal/handlers/ directory. Each handler package follows a consistent pattern:

  1. Define provider and generator interfaces in provider.go
  2. A handler struct that depends on these interfaces in handler.go
  3. A constructor function that injects the dependencies
  4. A method to register routes with a router
  5. Handler methods for various operations in separate files (e.g., create.go, list.go, etc.)

Example handler structure:

// Handler struct holds dependencies via interfaces
type Handler struct {
    dsProvider ProjectBriefQuestionDraftJobProvider
    generator  ContentDraftGenerator
    logger     *zap.Logger
}

// Constructor function with dependency injection
func NewHandler(dsProvider ProjectBriefQuestionDraftJobProvider, generator ContentDraftGenerator, logger *zap.Logger) *Handler {
    return &Handler{
        dsProvider: dsProvider,
        generator:  generator,
        logger:     logger,
    }
}

// Route registration method
func (h *Handler) RegisterRoutes(router *mux.Router) {
    resourceRouter := router.PathPrefix("/content-draft").Subrouter()
    
    resourceRouter.HandleFunc("", h.ListContentDrafts).Methods("GET")
    resourceRouter.HandleFunc("", h.CreateContentDraft).Methods("POST")
    resourceRouter.HandleFunc("/generate", h.GenerateContentDraft).Methods("POST")
    resourceRouter.HandleFunc("/{id}", h.GetContentDraft).Methods("GET")
    resourceRouter.HandleFunc("/{id}", h.UpdateContentDraft).Methods("PUT")
    resourceRouter.HandleFunc("/{id}", h.DeleteContentDraft).Methods("DELETE")
}

Path Parameter Handling

Gorilla Mux path variables should be accessed using mux.Vars(r):

// Get resource ID from URL
vars := mux.Vars(r)
idStr, ok := vars["id"]
if !ok || idStr == "" {
    h.respondWithError(w, http.StatusBadRequest, "Resource ID is required", nil)
    return
}

// Parse the ID
resourceID, err := uuid.Parse(idStr)
if err != nil {
    h.respondWithError(w, http.StatusBadRequest, "Invalid resource ID", err)
    return
}

Route Registration

Routes are registered in the setupRoutes method of the Server struct. The process follows these steps:

  1. Initialize data providers
  2. Initialize AI providers/generators
  3. Initialize handlers with providers and generators
  4. Create API router
  5. Set up public routes
  6. Set up protected routes with auth middleware
func (s *Server) setupRoutes() {
    // Create data providers
    dataStore := datastore.NewDatastore(s.db, s.logger)
    
    // Create AI providers
    aiProvider := ai.NewOpenAIProvider(s.logger)

    // Create handlers
    userHandler := user.NewUserHandler(s.db, s.logger)
    contentBriefHandler := contentbrief.NewHandler(dataStore, aiProvider, s.logger)
    contentDraftHandler := contentdraft.NewHandler(dataStore, aiProvider, s.logger)

    // Setup API routes
    apiRouter := s.router.PathPrefix("/api").Subrouter()

    // Health check
    apiRouter.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
        w.WriteHeader(http.StatusOK)
        w.Write([]byte("API is running"))
    }).Methods("GET")

    // Public routes
    apiRouter.HandleFunc("/users/register", userHandler.Register).Methods("POST")
    apiRouter.HandleFunc("/users/login", userHandler.Login).Methods("POST")

    // Protected routes
    authRouter := apiRouter.NewRoute().Subrouter()
    authRouter.Use(middleware.AuthMiddleware(s.db, s.logger))

    // Register protected routes
    userHandler.RegisterRoutes(authRouter)
    contentBriefHandler.RegisterRoutes(authRouter)
    contentDraftHandler.RegisterRoutes(authRouter)
}

Authentication

Authentication is handled using JWT tokens. The internal/auth package provides functions for:

  1. Generating access and refresh tokens
  2. Validating tokens
  3. Refreshing tokens
  4. Password hashing and verification
// Generate token pair
tokenPair, tokenID, err := auth.GenerateTokenPair(user.ID, user.Role)

// Validate access token
claims, err := auth.ValidateAccessToken(tokenString)

// Refresh access token
newAccessToken, err := auth.RefreshAccessToken(refreshToken)

// Hash password
hashedPassword, err := auth.HashPassword(password)

// Check password
isValid := auth.CheckPassword(password, hashedPassword)

Authentication Middleware

The authentication middleware extracts and validates the JWT token from the Authorization header and adds the user ID to the request context.

func AuthMiddleware(client *ent.Client, logger *zap.Logger) mux.MiddlewareFunc {
    return func(next http.Handler) http.Handler {
        return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
            authHeader := r.Header.Get("Authorization")
            if authHeader == "" {
                http.Error(w, "Unauthorized", http.StatusUnauthorized)
                return
            }

            // Extract the token
            tokenString := strings.Replace(authHeader, "Bearer ", "", 1)
            
            // Validate the token
            claims, err := auth.ValidateAccessToken(tokenString)
            if err != nil {
                http.Error(w, "Unauthorized", http.StatusUnauthorized)
                return
            }

            // Add user ID to context
            ctx := context.WithValue(r.Context(), UserIDKey, claims.UserID)
            
            // Call the next handler with the updated context
            next.ServeHTTP(w, r.WithContext(ctx))
        })
    }
}

Handler Utilities

The internal/handlers/handlerutils package provides common utilities for handlers:

HTTP Response Utilities

// Respond w

Truncated for display — read the full file on GitHub.

Related Skills

View on GitHub
GitHub Stars0
CategoryDevelopment
Updated9d ago
Forks0

Trust signals

80/100

From repository metadata: license, adoption, age and documentation. Not a code audit — see the Safety scan above for what the skill file itself contains.

1 medium1 low