Xortrix
← Back to Blog

Why We Built ITINE on AWS Amplify

8 min read

The Starting Point

When we started building ITINE — an AI-powered travel itinerary platform — we were a small team with a long list of requirements and a tight timeline. The product needed a web app, a mobile app, user authentication, a GraphQL API, serverless functions for AI processing, and a database. All of it had to be manageable by a lean engineering team.

That constraint shaped every architecture decision. We didn't need the most elegant system. We needed one we could actually ship and maintain without burning out on infrastructure plumbing.

Why Amplify?

We didn't choose AWS Amplify because it's the "best" serverless platform. We chose it because it's practical for a small team shipping fast. Amplify bundles hosting, auth (Cognito), API (AppSync/GraphQL), Lambda functions, and storage under one CLI. The alternative was wiring up each service individually with CDK or Terraform — totally doable, but significantly slower when your engineering bandwidth is focused on the product, not infrastructure.

The pitch is simple: a few CLI commands and you have a user pool, a GraphQL API with auto-generated resolvers, and a managed database. Is it magic? No. But it removes enough friction that you spend your time on the product instead of CloudFormation templates.

What Worked Well

AppSync + DynamoDB.Defining a GraphQL schema and getting auto-generated resolvers was a huge time saver. DynamoDB's flexible schema turned out to be a good fit for our data model, where different records have different shapes and the access patterns are well-defined upfront.

Cognito for auth.Email/password registration, social login, JWT tokens that AppSync validates automatically. We didn't have to build a token refresh flow, a password reset page, or an email verification system from scratch. One less thing to worry about at 2 AM when you're debugging a production issue.

Lambda for AI processing.Each AI generation request runs in a Lambda function. Cold starts add roughly 1–2 seconds, but since the AI processing itself takes several seconds, the cold start is invisible to users. They never notice it.

Amplify Hosting.Git-push deploys work exactly as advertised. Push to main and it builds and deploys to production. Push to a feature branch and you get a separate environment with its own URL and backend resources. Branch-based environments are genuinely useful when you're testing a major feature without risking production data.

What Surprised Us

DynamoDB requires thinking in access patterns, not schemas.Coming from PostgreSQL, our first instinct was to model entities and relationships. That's exactly wrong for DynamoDB. You start with the questions your app asks and design your keys backward from those queries. We had to redesign our table structure more than once as we added new features.

AppSync's VTL resolvers are painful to debug.Velocity Template Language is AWS's choice for writing resolver logic, and it's a templating language being forced into a programming role. Error messages are cryptic. There's no local debugger. We eventually moved all complex logic to Lambda resolvers, which are just Node.js functions you can test locally. VTL resolvers remain only for simple CRUD operations where the auto-generated ones work fine.

Amplify Gen 2 arrived mid-project.AWS released a significant overhaul of Amplify's configuration model while we were in active development. Gen 1 uses a CLI-driven workflow with JSON config files. Gen 2 uses TypeScript-based configuration with CDK under the hood. The migration wasn't trivial — it touched our CI/CD pipeline, environment variables, and how backend resources were referenced in frontend code. It took about two weeks, but the Gen 2 model is genuinely better for long-term maintenance.

What We'd Do Differently

Start with Lambda resolvers from day one. The auto-generated VTL resolvers look convenient at first, but the moment you need conditional logic, data transformation, or calls to external services, you hit a wall. Lambda resolvers are just functions. You can test them locally, log whatever you want, and use any npm package. The slight latency overhead is worth the developer experience.

Invest in DynamoDB data modeling upfront.We learned this the hard way. Our first design required redesigns every time we added a new access pattern. Spending a week on data modeling before writing any code would have saved us significantly more time downstream. Rick Houlihan's re:Invent talks on DynamoDB single-table design are essential viewing if you go this route.

Consider containers for AI processing if latency is critical.Lambda works well when the AI processing time masks the cold start. But in scenarios where you have concurrent users all triggering requests at once, cold starts can stack up. A container-based solution like AppRunner keeps instances warm and eliminates cold starts entirely. The trade-off is cost — you pay for idle time — but for a latency-sensitive feature, it might be worth it.

The Bottom Line

For a small team shipping a real product, Amplify removed enough friction to be worth the trade-offs. It's not perfect. The abstractions leak in places, the documentation has gaps, and you'll occasionally fight the framework instead of your actual problem. But it let a lean team build and ship a full-stack AI product to production — with auth, a GraphQL API, serverless compute, and a managed database — without getting stuck on infrastructure.

The question isn't whether Amplify is the best tool. It's whether it lets you ship. For us, it did.

We help teams ship production software — from serverless architectures and AI features to cross-platform mobile apps. If you're building something and need engineering help, let's talk.