Skip to content

feat(geocoding): add server-side geocoding for AI agents / API usage#73

Closed
jhb-dev wants to merge 9 commits intomainfrom
claude/agent-friendly-geocoding-EH8p3
Closed

feat(geocoding): add server-side geocoding for AI agents / API usage#73
jhb-dev wants to merge 9 commits intomainfrom
claude/agent-friendly-geocoding-EH8p3

Conversation

@jhb-dev
Copy link
Copy Markdown
Contributor

@jhb-dev jhb-dev commented Apr 6, 2026

Summary

The geocoding plugin was purely UI-driven (browser-based Google Places autocomplete), making it unusable for AI agents and API consumers. This PR adds two server-side geocoding mechanisms:

  • GET /api/geocoding-plugin/search?q=<address> — Authenticated endpoint for server-side geocoding with custom access function support
  • {field}_address beforeChange hook — Every geocodingField now includes a hidden _address text field; submitting an address string via the API auto-geocodes it and populates the point + geodata fields
  • geocodeAddress() exported service — Can be used directly in custom server-side code

Key implementation details

  • Endpoint defaults to requiring req.user (authenticated), supports geocodingEndpoint.access: ({ req }) => boolean for custom access control
  • Hook reads API key from plugin config (req.payload.config.custom) — no duplicate config needed
  • Address field is hidden from admin UI (admin.hidden: true) and cleared on save (beforeChange: [() => null])
  • Uses req.context to cache geocoding promises so the geodata and point field hooks share a single API call (Payload processes row fields in parallel via Promise.all)

Files added/changed

  • src/services/googleGeocoding.ts — Server-side Google Geocoding HTTP API client
  • src/endpoints/geocodingSearch.ts — Authenticated search endpoint
  • src/hooks/geocodeBeforeChange.ts — Geodata + point field beforeChange hooks with shared caching
  • src/fields/geocodingField.ts — Always includes address field + hooks
  • src/plugin.ts — Registers endpoint, passes custom access function
  • src/types/ — Updated config types with GeocodingEndpointAccess
  • dev/ — Full integration test setup with SQLite, Articles collection with Lexical block
  • .github/workflows/ci.yml — Added test-geocoding CI job
  • README.md — New "Usage with AI Agents / API" section

Test plan

  • 28 unit tests (service, endpoint, hooks, plugin, field config)
  • 10 integration tests against real Payload + SQLite:
    • Field structure: point/geodata in top-level, group, and array fields
    • Server-side geocoding: auto-geocode via _address on create and update
    • Lexical block: geocoding field inside a Lexical editor block
    • Endpoint registration verification
  • Type check passes (tsc --noEmit)
  • Lint passes (0 errors)
  • Formatting clean (prettier)
  • CI test-geocoding job passes

claude added 9 commits April 6, 2026 11:06
…hook for agent/API usage

The geocoding plugin was purely UI-driven, requiring browser-based Google Places
autocomplete. This made it unusable for AI agents and API consumers.

Adds:
- GET /api/geocoding/search?q=<address> authenticated endpoint with custom access function support
- beforeChange hook that auto-geocodes a `{field}_address` text string server-side
- geocodeAddress() service using Google Geocoding HTTP API
- 24 unit tests covering service, endpoint, hook, plugin, and field integration

https://claude.ai/code/session_01Ja3JVC48ozET22Z7yJkyY6
Explains the two server-side geocoding mechanisms (search endpoint and
beforeChange hook) with example requests so agents and API consumers
know how to populate geocoding fields without the browser UI.

https://claude.ai/code/session_01Ja3JVC48ozET22Z7yJkyY6
…fig, hide address field

- Remove `serverGeocoding` field config — the address field and
  beforeChange hook are now always included on every geocodingField
- Remove duplicate API key from hook — reads it from the plugin config
  via `req.payload.config.custom` instead
- Hide `{field}_address` text field from the admin UI (`admin.hidden: true`)
  since it's only for API/agent usage
- Update README to reflect the simpler setup (no extra config needed)
- Add test for missing API key error

https://claude.ai/code/session_01Ja3JVC48ozET22Z7yJkyY6
…tion signature

- Endpoint path: /geocoding/search → /geocoding-plugin/search
  (matches alt-text-plugin/ and translator/ conventions)
- Access function signature: (req) → ({ req }) to match other plugins
- Remove redundant sentence from README

https://claude.ai/code/session_01Ja3JVC48ozET22Z7yJkyY6
…v project + SQLite

Major changes:
- Set up dev project test infrastructure (vitest, SQLite adapter, vite.config)
- Add Articles collection with Lexical editor block containing geocoding field
- Write 10 integration tests against a real Payload instance:
  - Field structure: point/geodata in top-level, group, and array fields
  - Server-side geocoding: auto-geocode via location_address on create/update
  - Lexical block: geocoding field inside a Lexical editor block
  - Endpoint: verify geocoding-plugin/search is registered
- Fix parallel field processing bug: use req.context to share a cached
  geocoding promise between geodata and point field beforeChange hooks
  (Payload processes row fields concurrently via Promise.all)
- Remove old mongoose-based plugin.spec.ts

https://claude.ai/code/session_01Ja3JVC48ozET22Z7yJkyY6
- Add test-geocoding job to CI workflow (SQLite only)
- Add test/test:sqlite scripts to geocoding package.json
- Apply lint:fix and prettier formatting to all source files

https://claude.ai/code/session_01Ja3JVC48ozET22Z7yJkyY6
- Remove duplicate GeocodingEndpointAccess type from geocodingSearch.ts,
  import from GeoCodingPluginConfig.ts instead
- Add virtual: true to the address field so no DB column is created
- Keep beforeChange hook as fallback for contexts where virtual doesn't
  apply (Lexical blocks store fields as JSON)

https://claude.ai/code/session_01Ja3JVC48ozET22Z7yJkyY6
…ual field bug

The address field uses virtual: true which correctly prevents DB
persistence for regular fields. Inside Lexical blocks, the virtual
flag is ignored because blocks serialize all fields as JSON — this
is a Payload bug, not something to work around with a hook.

https://claude.ai/code/session_01Ja3JVC48ozET22Z7yJkyY6
Copy link
Copy Markdown
Contributor Author

jhb-dev commented Apr 7, 2026

Closing in favor of #78 — the server-side geocoding features (endpoint, beforeChange hooks, tests, CI) have been merged into the 0.3 branch (feat/geocoding-places-api-new) so everything ships as a single release with the _meta field rename and Places API (New) migration.


Generated by Claude Code

@jhb-dev jhb-dev closed this Apr 7, 2026
@jhb-dev jhb-dev deleted the claude/agent-friendly-geocoding-EH8p3 branch April 7, 2026 21:26
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.

2 participants