Skip to main content

AST Parsing System

The AST (Abstract Syntax Tree) parsing system is the core of pb-ext’s automatic OpenAPI documentation generation. It analyzes your Go source code at startup to extract handler metadata, request/response schemas, and parameters without requiring manual annotations.

Overview

The AST parser uses Go’s go/ast and go/parser packages to analyze source files marked with the // API_SOURCE directive. It extracts:
  • Handler function signatures and return types
  • Request body types (from BindBody and json.Decode)
  • Response schemas (from c.JSON() calls)
  • Query, header, and path parameters
  • Authentication requirements
  • Struct definitions for component schemas

File Structure

The AST parser is split across multiple files by responsibility:

Handler Detection

A function is recognized as a PocketBase handler if it matches this exact signature:
VariableExprs: map[string]ast.Expr — variable name → RHS AST node for deep analysis
MapAdditions: map[string][]MapKeyAdd — dynamic mapVar["key"] = value assignments

Two-Pass Struct Extraction

Struct extraction uses a two-pass approach to handle cross-references correctly: Pass 1: Register all structs with fields (no schemas) and type aliases
Pass 2: Generate JSONSchema for each struct now that all names are known
  • Resolves $ref pointers to other structs
  • Flattens embedded struct fields
  • Handles pointer fields with nullable: true
Changing to single-pass will break nested struct $ref resolution. Do not modify this behavior.

Request Detection

The parser detects request bodies from these patterns:
The type is resolved from the variable’s tracked type in handlerInfo.Variables.

Response Detection

Response schemas are extracted from c.JSON(status, expr) calls:
Analysis steps:
  1. Try composite literal analysis (map/struct/slice)
  2. If argument is a variable, trace to its stored expression
  3. Merge any MapAdditions for that variable
  4. Fall back to type inference → $ref for known structs
  5. Last resort: generic object schema

Parameter Detection

The parser detects parameters in two passes:

Pass 1: Direct Body Scan

Pass 2: Indirect Helper Scan

The parser automatically detects parameters read by helper functions: Domain helpers — literal param names:
Generic helpers — param name from call site:

Function Return Type Resolution

extractFuncReturnTypes() runs before handler analysis to enable type inference:
Stored in ASTParser.funcReturnTypes as map[string]string (func name → Go type).

Helper Function Body Analysis

For functions returning map[string]any or []map[string]any, the parser deep-analyzes the function body:
How it works:
  1. Creates temporary ASTHandlerInfo to track variables
  2. Finds all map[string]any{...} literals
  3. Picks the literal with most keys (primary response shape)
  4. Finds variable name via findAssignedVariable()
  5. Merges dynamic mapVar["key"] = value additions
  6. For []map[string]any, wraps item schema in array
Results stored in funcBodySchemas for reuse during response analysis.

Append-Based Slice Resolution

When handlers build slices via append(), the parser connects the item expression:
How it works:
  1. trackVariableAssignment() detects varName = append(varName, itemExpr)
  2. Stores itemExpr in SliceAppendExprs[varName]
  3. enrichArraySchemaFromAppend() resolves item schema from stored expression

Auto-Import Following

After parsing all // API_SOURCE files, the parser automatically resolves local imports to find struct definitions:
Process:
  1. Reads go.mod for module path (e.g., github.com/user/myapp)
  2. Collects imports from all // API_SOURCE files
  3. Strips module prefix → local directory path
  4. Skips already-parsed directories
  5. Calls parseDirectoryStructs() to extract structs only (no handlers)
Zero-config — no directives needed on type files. External imports are ignored.

Source File Directives

Example:

Debug Endpoint

Inspect the full AST parser state at runtime:
Returns:
  • All parsed structs and their schemas
  • All detected handlers and their metadata
  • Per-version endpoints
  • Component schemas
  • Complete OpenAPI output
Requires authentication. Use superuser credentials.

Common Patterns

Anonymous Struct Request Body

The parser generates inline schema from the anonymous struct definition.

Index Expression Resolution

When a helper reads from another helper’s return:

Variable Tracing

Parser Lifecycle

Performance

AST parsing happens once at startup. Specs are cached in memory. For production builds, use pre-generated specs from disk (see Spec Generation).

Further Reading