Core Concepts

Understanding how Bytedocs works under the hood

Understanding these core concepts will help you get the most out of Bytedocs and troubleshoot any issues.

How Bytedocs Works

Bytedocs uses a multi-stage process to generate your API documentation:

Rendering Chart

1. Source Code Analysis

When Bytedocs starts, it analyzes your source code using Abstract Syntax Trees (AST):

  • Go: Uses go/parser and go/ast packages
  • PHP: Uses PHP Reflection API and AST parser
  • Node.js: Uses TypeScript compiler or Babel parser
  • Python: Uses ast module
  • Rust: Uses syn crate for syntax analysis

2. Route Discovery

Bytedocs connects to your framework's routing system:

Go (Gin example):

routes := engine.Routes()
for _, route := range routes {
    // Extract: method, path, handler
}

Laravel:

$routes = Route::getRoutes();
foreach ($routes as $route) {
    // Extract: method, URI, action, middleware
}

3. Handler Analysis

For each route, Bytedocs analyzes the handler function:

  • Parameters: Path params, query strings, headers
  • Request Body: Types, validation rules, required fields
  • Response: Status codes, return types, examples
  • Documentation: Comments, annotations, docblocks

4. Type Inference

Bytedocs builds complete schemas by analyzing types:

Example (Go):

type User struct {
    ID    int    `json:"id" example:"123"`
    Name  string `json:"name" binding:"required"`
    Email string `json:"email" validate:"email"`
}

Extracted Schema:

{
  "type": "object",
  "properties": {
    "id": {
      "type": "integer",
      "example": 123
    },
    "name": {
      "type": "string"
    },
    "email": {
      "type": "string",
      "format": "email"
    }
  },
  "required": ["name", "email"]
}

5. Schema Generation

All extracted information is converted into OpenAPI 3.0.3 schemas:

  • Paths: All routes with parameters
  • Components: Reusable schemas
  • Security: Authentication schemes
  • Servers: Multiple environment URLs

Auto-Detection Mechanism

How Auto-Detection Works

Step 1: Register Framework Hook

When you call SetupGinDocs() or equivalent:

// Bytedocs registers a catch-all route
router.Any("/docs/*path", docsHandler)

Step 2: Lazy Analysis

On first request to /docs:

  1. Get all registered routes from framework
  2. For each route, find the handler function
  3. Locate source file using reflection
  4. Parse source code with AST
  5. Extract metadata (params, body, response)
  6. Build schema from extracted data
  7. Cache results for subsequent requests

Step 3: Serve Documentation

The generated documentation is:

  • Cached in memory
  • Served as JSON via API
  • Rendered in React UI

What Gets Auto-Detected

Automatically Detected:

  • HTTP methods (GET, POST, PUT, DELETE, PATCH)
  • Route paths with parameters
  • Path parameters (:id, {id})
  • Query parameters (from handler code)
  • Request headers (from handler code)
  • Request body types and validation
  • Response types and status codes
  • Nested object structures
  • Array and map types
  • Enum types
  • Required vs optional fields

Not Auto-Detected (requires annotations):

  • Complex business logic descriptions
  • Custom authentication schemes
  • Rate limiting information
  • Deprecation notices
  • External API dependencies

Request/Response Detection

Request Body Detection

Bytedocs detects request bodies by analyzing binding calls:

Go (Gin):

func createUser(c *gin.Context) {
    var req CreateUserRequest
    c.BindJSON(&req)  // <-- Detected here
    // ...
}

Laravel:

public function store(StoreUserRequest $request)
{
    // FormRequest automatically detected
}

Node.js (Express):

app.post('/users', (req, res) => {
    const { name, email } = req.body;  // Detected from usage
    // ...
});

Response Detection

Status Codes:

c.JSON(200, user)  // Status: 200
c.JSON(201, user)  // Status: 201
c.JSON(http.StatusNotFound, err)  // Status: 404

Response Types:

func getUser(c *gin.Context) {
    user := User{...}
    c.JSON(200, user)  // Response type: User
}

func getUsers(c *gin.Context) {
    users := []User{...}
    c.JSON(200, users)  // Response type: User[]
}

Multiple Response Types:

func getUser(c *gin.Context) {
    user, err := userService.Get(id)
    if err != nil {
        c.JSON(404, ErrorResponse{Message: "Not found"})
        return
    }
    c.JSON(200, user)
}
// Detected responses:
// - 200: User
// - 404: ErrorResponse

Struct Tag System

Supported Tags

JSON Tags

type User struct {
    ID   int    `json:"id"`            // Field name
    Name string `json:"name"`
    Age  int    `json:"age,omitempty"` // Optional field
    Pass string `json:"-"`             // Excluded from JSON
}

Example Tags

type User struct {
    ID    int    `json:"id" example:"123"`
    Name  string `json:"name" example:"John Doe"`
    Email string `json:"email" example:"john@example.com"`
}

Validation Tags

type CreateUser struct {
    Name  string `json:"name" binding:"required"`
    Email string `json:"email" binding:"required,email"`
    Age   int    `json:"age" binding:"min=0,max=150"`
}

Format Tags

type User struct {
    ID        int       `json:"id" format:"int64"`
    Email     string    `json:"email" format:"email"`
    Website   string    `json:"website" format:"uri"`
    BirthDate time.Time `json:"birth_date" format:"date-time"`
}

Description Tags

type User struct {
    ID    int    `json:"id" description:"Unique user identifier"`
    Name  string `json:"name" description:"User's full name"`
    Email string `json:"email" description:"Email address (must be unique)"`
}

Tag Processing Order

  1. JSON tag - Determines field name
  2. Example tag - Sets example value
  3. Binding/Validation tags - Marks required, adds constraints
  4. Format tag - Sets OpenAPI format
  5. Description tag - Adds field description
  6. Comments - Fallback description source

OpenAPI Schema Generation

Generated OpenAPI Structure

openapi: 3.0.3
info:
  title: My API
  version: 1.0.0
  description: API documentation

servers:
  - url: https://api.example.com
    description: Production
  - url: http://localhost:8080
    description: Local

paths:
  /users/{id}:
    get:
      summary: Get user by ID
      tags: [Users]
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: Success
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/User'

components:
  schemas:
    User:
      type: object
      properties:
        id:
          type: integer
        name:
          type: string
        email:
          type: string
          format: email
      required: [name, email]

Schema Reusability

Bytedocs automatically identifies reusable schemas:

// Used in multiple endpoints
type User struct {
    ID   int    `json:"id"`
    Name string `json:"name"`
}

// Referenced as: #/components/schemas/User
// Instead of duplicated inline schemas

Caching Strategy

Memory Cache

Generated documentation is cached in memory:

var (
    docCache      *Documentation
    docCacheMutex sync.RWMutex
    docCacheTime  time.Time
)

Cache Invalidation

Cache is cleared when:

  1. Application restarts
  2. Config changes detected
  3. Manual clear command (Laravel: php artisan bytedocs:clear)

Production Optimization

In production, disable auto-detect for better performance:

config := &core.Config{
    AutoDetect: false,  // Use pre-generated docs
}

Pre-generate documentation:

bytedocs generate --output=docs.json

Error Handling

Common Error Scenarios

1. Handler Not Found

Cause: Handler function source file not accessible

Solution: Ensure handler files are in the same module

2. Type Not Resolved

Cause: Complex type or external package

Solution: Use manual schema registration

3. Circular Reference

Cause: Recursive struct definitions

Solution: Bytedocs automatically detects and handles with $ref

Debug Mode

Enable debug logging:

config := &core.Config{
    Debug: true,
}

This will log:

  • Routes discovered
  • Handler analysis results
  • Schema generation steps
  • Errors and warnings

Performance Considerations

Analysis Performance

  • First Request: 100-500ms (depending on API size)
  • Subsequent Requests: < 1ms (served from cache)
  • Large APIs: 1000+ endpoints analyzed in < 2 seconds

Memory Usage

  • Small API (< 50 endpoints): ~5MB
  • Medium API (50-200 endpoints): ~15MB
  • Large API (200-1000 endpoints): ~50MB

Optimization Tips

  1. Use caching in production
  2. Exclude unnecessary routes
  3. Pre-generate documentation
  4. Disable auto-detect in production
  5. Use manual registration for complex routes

Security Considerations

What Bytedocs Exposes

Exposed:

  • Route paths and methods
  • Parameter names and types
  • Request/response schemas
  • Validation rules

Not Exposed:

  • Handler implementation logic
  • Database queries
  • Business logic
  • Secrets or credentials
  • Internal variable names

Protecting Documentation

  1. Enable Authentication:
config.AuthConfig = &core.AuthConfig{
    Enabled: true,
    Type: "session",
    Password: "secret",
}
  1. IP Whitelisting:
config.AuthConfig.AdminWhitelistIPs = []string{
    "10.0.0.0/8",
    "192.168.0.0/16",
}
  1. Disable in Production:
if env == "production" {
    config.Enabled = false
}

What's Next?