Skip to content

feat(agent): support get-one projection via the Forest-Projection header - #1813

Open
PMerlet wants to merge 1 commit into
mainfrom
feature/prd-892-move-get-one-projection-from-query-string-to-a-dedicated
Open

feat(agent): support get-one projection via the Forest-Projection header#1813
PMerlet wants to merge 1 commit into
mainfrom
feature/prd-892-move-get-one-projection-from-query-string-to-a-dedicated

Conversation

@PMerlet

@PMerlet PMerlet commented Aug 7, 2026

Copy link
Copy Markdown
Member

Motivation

The get-one projection currently travels in the query string (fields[Collection]=...&fields[relation]=id). On large collections it exceeds query string size limits enforced by common WAF configurations (e.g. the AWS WAF managed rule SizeRestrictions_QUERYSTRING, 2,048 bytes), which blocks the CORS preflight and breaks the details view.

fixes PRD-892

Changes

  • QueryStringParser.parseProjectionFromHeader: reads the projection from the Forest-Projection header. Contract:
    • format field1,field2,relation:subfield (e.g. id,title,author:id), whitespace around commas tolerated, repeated header supported;
    • empty/whitespace-only header treated as absent;
    • one relation level max — nested projections (a:b:c) are rejected, strict parity with what fields[...] can express;
    • invalid header → 400 with no silent fallback to query params, so frontend bugs surface instead of being masked.
  • Get-one route: the header takes precedence over the fields[...] query params; the query-string parsing is kept as fallback.
  • Capabilities: the agent announces canUseProjectionViaHeader: true (next to canUseProjectionOnGetOne, which is kept — the frontend does the prioritization).
  • CORS: no change needed — @koa/cors without an allowHeaders option echoes Access-Control-Request-Headers, so the preflight already allows the header. An integration test pins this across all 8 supported mount targets (standalone, express, koa, fastify v2/v3/v4, nestjs express/fastify).

Tests

  • Unit tests on the parser (missing/empty/whitespace header, repeated header as array, duplicates, trailing comma, subfield on a column, nested projection rejection, unknown field).
  • Route tests: header honored, precedence over query params, no fallback on invalid header, fallback on empty header.
  • End-to-end integration tests on all 8 frameworks: real GET with the header returns only the projected fields; invalid header returns 400; CORS preflight allows the header.

🤖 Generated with Claude Code

Note

Add Forest-Projection header support for GET one record field selection

  • GetRoute.handleGet reads projection from the Forest-Projection HTTP header first, falling back to query string params when the header is absent or empty; primary keys are always appended via projection.withPks.
  • QueryStringParser.parseProjectionFromHeader is a new static method that parses and validates the header value, supporting single-level relation notation (e.g. relation:field) and throwing a ValidationError for invalid or deeply nested paths.
  • The /_internal/capabilities endpoint now includes canUseProjectionViaHeader: true in agentCapabilities to signal this feature to clients.

Macroscope summarized bceed7c.

The get-one route now reads the projection from the Forest-Projection
header (format: `field1,field2,relation:subfield`), taking precedence
over the `fields[...]` query params. This lets the frontend send large
projections without hitting query string size limits enforced by
common WAF rules (e.g. AWS WAF SizeRestrictions_QUERYSTRING, 2KB).

The agent announces a new `canUseProjectionViaHeader` capability so the
frontend only sends the header to agents that understand it. The
existing query-string parsing is kept as fallback.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@linear-code

linear-code Bot commented Aug 7, 2026

Copy link
Copy Markdown

PRD-892

@qltysh

qltysh Bot commented Aug 7, 2026

Copy link
Copy Markdown

1 new issue

Tool Category Rule Count
qlty Structure Function with many returns (count = 4): fetchCapabilities 1

@qltysh

qltysh Bot commented Aug 7, 2026

Copy link
Copy Markdown

Qlty


Coverage Impact

This PR will not change total coverage.

Modified Files with Diff Coverage (2)

RatingFile% DiffUncovered Line #s
Coverage rating: A Coverage rating: A
packages/agent/src/utils/query-string.ts100.0%
Coverage rating: A Coverage rating: A
packages/agent/src/routes/access/get.ts100.0%
Total100.0%
🚦 See full report on Qlty Cloud »

🛟 Help
  • Diff Coverage: Coverage for added or modified lines of code (excludes deleted files). Learn more.

  • Total Coverage: Coverage for the whole repository, calculated as the sum of all File Coverage. Learn more.

  • File Coverage: Covered Lines divided by Covered Lines plus Missed Lines. (Excludes non-executable lines including blank lines and comments.)

    • Indirect Changes: Changes to File Coverage for files that were not modified in this PR. Learn more.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant