Skip to content

[go-fan] Go Module Review: itchyny/gojq #4177

@github-actions

Description

@github-actions

🐹 Go Fan Report: itchyny/gojq

Module Overview

github.com/itchyny/gojq (v0.12.19) is a pure-Go implementation of the jq JSON processor with an embeddable API. It enables programmatic jq query execution without spawning external processes — offering full jq language compatibility plus Go-native extensions like context-aware cancellation, custom function registration, and a typed HaltError.

Current Usage in gh-aw-mcpg

The module powers the jq schema middleware in internal/middleware/jqschema.go. When a tool-call response exceeds the configured sizeThreshold, the middleware:

  1. Saves the full JSON payload to {payloadDir}/{sessionID}/{queryID}/payload.json
  2. Runs a custom walk_schema jq filter to produce a type-only schema (leaf values replaced with their JSON type names)
  3. Returns a PayloadMetadata struct containing the payload path, a 500-char preview, and the schema
  • Files: 2 (jqschema.go, jqschema_bench_test.go)
  • Import Count: 2 files
  • Key APIs Used: gojq.Parse, gojq.Compile, gojq.Code, (*Code).RunWithContext, gojq.HaltError

Research Findings

What the Project Does Well 🌟

The implementation is already a textbook example of gojq best practices:

Practice Status
Compile once at init(), reuse *gojq.Code for all requests
Use RunWithContext for cancellation + timeout propagation
Handle gojq.HaltError with typed assertion, not just error
Single-output contract tested (TestApplyJqSchema_SingleOutputContract)
Custom walk_schema instead of built-in walk(f) — with thorough comments explaining behavioral differences
Benchmark tests comparing compiled vs. parse-every-call approaches
UTF-8 safe payload truncation at rune boundaries
Avoid JSON round-trip for native map[string]interface{} / []interface{}

Recent Updates (v0.12.x)

  • Enhanced array handling (up to 2^29 = ~536M elements)
  • Improved concurrent execution performance
  • Better type-error messages
  • timefmt-go integration for strftime/strptime support

Improvement Opportunities

🏃 Quick Wins

1. Extend native type passthrough to scalar types

The current code avoids JSON round-tripping for map and slice types but falls back to json.Unmarshal for all others — including scalars that gojq can accept natively:

// Current (jqschema.go ~line 357)
switch data.(type) {
case map[string]interface{}, []interface{}:
    jsonData = data
default:
    json.Unmarshal(payloadJSON, &jsonData)
}

// Improved: also pass scalars natively
switch data.(type) {
case map[string]interface{}, []interface{},
     string, float64, bool, nil:
    jsonData = data
default:
    json.Unmarshal(payloadJSON, &jsonData)
}

2. Decouple version-specific comment from module version

The jqSchemaFilter const block includes // gojq v0.12.19 features including: — this will drift silently when the module is upgraded. Phrasing it more generically (or removing the version pinning from the comment) keeps docs accurate across upgrades.

3. Document iterator non-draining

After the first iter.Next() call the iterator is abandoned. This is safe (gojq iterators are synchronous — no goroutines), but a one-line comment prevents future confusion:

// No further drain needed: walk_schema produces exactly one value and gojq
// iterators are synchronous (no background goroutines to clean up).

✨ Feature Opportunities

gojq.WithVariables for configurable schema depth

If deep or adversarial payloads ever become a concern, the filter could accept a $maxDepth variable:

jqSchemaCode, err = gojq.Compile(query, gojq.WithVariables([]string{"$maxDepth"}))
// ...
iter := jqSchemaCode.RunWithContext(ctx, jsonData, maxDepthValue)

This enables dynamic configuration without rewriting the filter string.

gojq.WithFunction for custom Go-backed helpers

Custom functions registered via gojq.WithFunction run at native Go speed inside jq pipelines — useful for future extensions like hash computation, size-aware truncation, or custom type formatting inside the schema filter.

gojq.TypeConverter for non-standard response types

If custom Go types (int64, time.Time, domain structs) ever appear in the middleware data pipeline, implementing the gojq.TypeConverter interface avoids the JSON fallback path entirely and enables type-safe jq processing.

📐 Best Practice Alignment

Startup failure visibility

If init() fails to compile the filter (jqSchemaCompileErr != nil), the application starts silently and fails at the first request. A guard at cmd startup time would surface the issue immediately:

// In cmd/root.go or server startup
if middleware.JqSchemaCompileErr() != nil {
    log.Fatalf("jq schema filter failed to compile: %v", middleware.JqSchemaCompileErr())
}

This converts a latent runtime failure into a fast, visible startup failure.

🔧 General Improvements

The middleware is well-structured with no redundant gojq usage. No dead code or duplicated query logic was found.

Recommendations

Priority Action
Low Add comment explaining iterator non-draining (1 line)
Low Decouple version-specific comment in jqSchemaFilter
Low Extend native type passthrough to scalars
Medium Add startup guard for jqSchemaCompileErr
Future Explore gojq.WithVariables if schema depth limits become needed

Next Steps

  • Consider a follow-up PR for the startup-guard improvement — it improves operator experience at near-zero cost
  • The iterator non-draining comment is a trivial 1-line addition worth doing in the next touch of jqschema.go
  • No version upgrade needed: v0.12.19 is current and the usage is optimal

Generated by Go Fan 🐹
Module summary saved to: specs/mods/gojq.md (session artifact — specs/mods/ directory creation pending repository write access)
Workflow run: §24654504583

Note

🔒 Integrity filter blocked 8 items

The following items were blocked because they don't meet the GitHub integrity level.

To allow these resources, lower min-integrity in your GitHub frontmatter:

tools:
  github:
    min-integrity: approved  # merged | approved | unapproved | none

Generated by Go Fan · ● 1.4M ·

  • expires on Apr 27, 2026, 7:48 AM UTC

Metadata

Metadata

Assignees

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions