A decentralized exchange and liquid-NFT protocol lives on-chain, but the chain is only half the product. The other half is a cloud backend that has to be fast where the chain is slow, and cost almost nothing when nobody is trading.
The challenge
The protocol had three moving parts: a marketing site, an exchange web app with wallet connectivity, and a backend that read state from the blockchain, indexed minted-NFT data, and served NFT media and metadata at scale. The team was small, so whatever got built had to run itself - and traffic to a DeFi frontend is the hard part. It is spiky, global, and unpredictable: quiet for hours, then a wall of users the moment a market moves or a mint opens. Provisioning for the peak wastes money the rest of the time; provisioning for the average falls over exactly when it matters.
The deeper problem sits at the boundary between two worlds. On-chain reads are slow, rate-limited, and not something a user-facing frontend should hit directly on every page load. The protocol needed a fast, cheap API in front of the chain - one that could sync minted-NFT data off-chain into a low-latency store, manage media in object storage, and expose clean endpoints to the frontends, reproducibly across environments and with no servers to babysit.
Our approach
The reframe was to stop treating the chain as the backend. The chain is the source of truth, but it is a poor place to read from at request time. So we put a serverless indexing layer between the two, and made the whole estate disposable code.
An indexing API between chain and UI
Rather than let the frontend wait on a slow on-chain call for every page load, the backend syncs on-chain data once and serves it many times. Focused Node Lambdas behind API Gateway each did one job: sync minted-NFT data from the chain, save mint metadata, sync and set NFT media, transfer and track NFTs, and serve minted-NFT queries. A web3 client library handled the on-chain reads; DynamoDB held the indexed account and minted-NFT tables; S3 stored the media. Users read from a low-latency store, never from the chain directly.
Serverless because the economics fit
Lambda plus DynamoDB plus S3 scales with demand and costs close to nothing at idle - exactly the shape of a lean protocol’s traffic and budget. There is no fleet to right-size and no idle capacity to pay for: the bill tracks usage, and the platform absorbs a mint-day spike without anyone being paged. CloudFront sat in front of both the static SPA builds and the media buckets for CDN-speed delivery globally.
Everything reproducible in Terraform
The foundation was a modular Terraform project: reusable modules for API Gateway and its alarms, the CloudFront CDN, CodeBuild, CodePipeline, S3, and a standard-secret pattern, composed across four environments (dev, sit, uat, prod), with remote state in S3 and DynamoDB locking. Because the estate is code, environments are reproducible and disposable rather than precious - the same definition stands up dev or prod, and nothing drifts between them.
Under the hood
The request and sync flow is deliberately simple:
- A wallet user hits CloudFront over HTTPS, which serves the static React apps from S3 and proxies API calls to API Gateway.
- API Gateway routes to the relevant Lambda - read a minted NFT, transfer one, serve media.
- For fresh state, a Lambda uses the web3 client to read the chain (owner-of-token, minted NFTs) and writes the indexed result into DynamoDB.
- NFT media and metadata live in S3, delivered through the same CloudFront distribution; CodeBuild and CodePipeline run CI/CD, with ACM and Route53 for certificates and DNS.
Two details made the difference day to day. To keep Terraform versions consistent across the team, we Dockerized the tooling - a containerized terraform wrapper plus tfswitch, driven by a Makefile - so every engineer and every pipeline ran the same toolchain. And because web3 backends are painful to iterate on, we wired up a local stack with LocalStack, a DynamoDB admin UI via docker-compose, and Serverless Offline, so the whole API could run on a laptop without spending testnet gas or fighting flaky public endpoints.
The outcome
Web3-grade traffic landed on infrastructure that costs in proportion to use and scales without intervention. Users never waited on a slow on-chain call for data that could be synced once and read many times. The multi-environment Terraform gave a reproducible promotion path from dev to prod with no “works on my machine” drift, and the local stack let developers iterate in seconds rather than block times. The chain got the reliable, reproducible cloud backend it needed - with no servers to babysit.
Key takeaways
- Index, do not proxy. For a web3 frontend, sync on-chain data once into a low-latency store and serve it fast, instead of reading the chain on every request.
- Serverless matches a lean team’s economics. Lambda, DynamoDB and S3 scale with spiky traffic and cost almost nothing at idle - no fleet to right-size.
- Make environments disposable. Modular, multi-environment Terraform with locked remote state turns dev-to-prod into a reproducible promotion, not a migration.
- Dockerize the toolchain. Pinning Terraform versions in a container removes an entire class of “works on my machine” drift across laptops and pipelines.
- Invest in a real local stack. LocalStack plus Serverless Offline beats iterating against a shared testnet, which is slow and expensive and shows.