Skip to content
All work

Case study

Unified Go backend and Cognito sign-in

Merged four product backends into one Go service and moved every application to AWS Cognito sign-in, with a scoped partner API at the edge.

Result
100K+ daily requests, P95 latency down 40%, and a production API adopted by FFC, Corteva, Engro and Kubota.
Stack
GoAWS CognitoOAuth 2.0PostGISAPI GatewayKubernetes

Context

Farmdar grew four products (YieldPro, CropScan, eSurvey and DeveloPro), each with its own backend, database and login. Every cross-product feature meant changes in several places, and users needed separate accounts.

How it works

Unified backend: Farmdar web and mobile apps sign in through the Cognito hosted UI and reach one Go backend through a load balancer; partners use API Gateway with a Cognito authorizer; one PostGIS database sits behind it.

One Go binary serves every product as a module under a single API, backed by one PostgreSQL/PostGIS database with an optional read-only pool for heavy queries. It runs on EKS in hardened pods: non-root, read-only file system and images pinned by digest.

Sign-in uses Cognito's hosted UI with the authorization-code flow. Mobile apps use PKCE, and staff sign in with Google Workspace through SAML. The Go service verifies tokens locally against cached signing keys (JWKS), with no network call per request. Behind the load balancer it verifies the load balancer's own signed token.

Partners use a separate entry point: API Gateway with a Cognito authorizer, machine-to-machine clients and read and write scopes, connected privately to the service. Only a few routes are exposed there.

Key decisions

  • Unify the data first

    The first release merged the four products' data into one schema and backend in a single large change, with migration scripts for each product. After that, new features were ordinary modules instead of cross-service projects.

  • Migrate auth behind a feature flag

    The Cognito rollout ran in three phases. For a while the backend accepted both old and Cognito tokens, using a cheap header check to tell them apart, and Cognito was switched on per environment. Deploying the code changed nothing until the flag was turned on.

  • The database decides who can see what

    Tokens prove identity. Organisation and group membership come from the database, cached for a few minutes, so access changes take effect without reissuing tokens.

  • Create the identity first, then the user

    New users are created in Cognito first and then in the database in one transaction. If the database step fails, the Cognito user is deleted, so the database never points to an identity that doesn't exist.

  • Timeouts per handler, not globally

    Exports and large GeoJSON responses can legitimately take longer than the gateway's 29-second limit. Instead of one global timeout, handlers validate early and set their own limits.

My role

I led the unification release and ran the Cognito migration from design to cutover, including multi-organisation switching, atomic user provisioning, the partner API and replacing static CI deploy keys with GitHub OIDC.