Get AI summaries of any video or article — Sign up free
This Is The PERFECT Tech Stack For a SaaS Product thumbnail

This Is The PERFECT Tech Stack For a SaaS Product

Simon Høiberg·
5 min read

Based on Simon Høiberg's video on YouTube. If you like this content, support the original creators by watching, liking and subscribing to their content.

TL;DR

Use DynamoDB for a schema-less, document-friendly data store that can evolve without heavy upfront modeling.

Briefing

A serverless AWS stack built with Pulumi, DynamoDB, AppSync, Cognito, Next.js, Chakra UI, and Amplify can get a SaaS product from zero to a working, authenticated GraphQL app in under an hour—often closer to 10 minutes using a ready boilerplate. The core idea is to avoid server management entirely and rely on managed services and generated scaffolding so early development focuses on product features, not infrastructure plumbing.

The backend is designed around pay-per-use and minimal operational overhead. DynamoDB serves as a schema-less, managed database where teams can store documents in a single field while still indexing entries via keys—making it easy to start simple and evolve the data model later. AppSync then provides a managed GraphQL layer: instead of running a custom API server, developers define a GraphQL schema and map queries to data sources. For authentication, Cognito handles the complexity of user sign-in, password rules, and account recovery, and it integrates cleanly with AppSync.

Infrastructure provisioning is handled with Pulumi, which lets developers define cloud resources as code. Using Pulumi with AWS, the stack can set up the DynamoDB table, AppSync GraphQL endpoint and schema, the Lambda-backed resolver path, and Cognito components (user pool, identity pool, and app client) in one repeatable deployment. The workflow is built for speed: code lives in a monorepo, and GitHub Actions is used for continuous deployment.

On the frontend, Next.js (with TypeScript) provides the application framework, while Chakra UI supplies prebuilt UI components to reduce design and layout work. Amplify connects the UI to the backend and streamlines authentication flows; it also generates GraphQL queries, mutations, and TypeScript types from the AppSync schema, keeping frontend and backend aligned as the schema evolves. For data fetching, the stack uses Apollo Client, with a custom fetch flow that attaches the JWT token to GraphQL requests so AppSync can authorize queries.

Cost concerns are addressed by leaning into serverless primitives: Lambda and AppSync operate on usage, and DynamoDB charges for read/write operations rather than always-on servers. The setup is positioned as “negligible” for early experimentation, with an additional note that AWS offers 1,000 in free credits to start.

After outlining the architecture, the walkthrough demonstrates how to implement it in Pulumi: create a DynamoDB table for user records, define a GraphQL schema with a query (e.g., fetching a user), wire AppSync to a Lambda data source via roles/policies and resolvers, and then configure Cognito pools and identity permissions. Once deployed, Pulumi outputs the IDs and endpoints needed for the frontend. The Next.js app then runs with Amplify configuration, Chakra-based auth pages (sign-in, sign-up, confirmation), and Apollo-powered GraphQL queries.

The practical takeaway is a reusable SaaS boilerplate: a full-stack, authenticated GraphQL app scaffold that can be spun up quickly from a clean slate, then extended by adding more GraphQL fields, resolvers, and UI pages—without redoing the foundational infrastructure each time.

Cornell Notes

The stack targets rapid SaaS startup by combining managed AWS services with infrastructure-as-code. DynamoDB provides a schema-less, document-friendly database; AppSync supplies a managed GraphQL API; Cognito handles authentication; and Lambda acts as the resolver logic behind GraphQL fields. Pulumi provisions all of these resources in a repeatable way, while Next.js plus Chakra UI accelerates the frontend. Amplify connects the frontend to AppSync and Cognito, and it can auto-generate GraphQL types from the schema; Apollo Client then performs authenticated GraphQL queries using JWT tokens. This matters because it shifts effort from server management and custom API plumbing to product development, with usage-based costs instead of always-on infrastructure.

Why does the backend start with DynamoDB instead of a traditional relational database?

DynamoDB is managed and schema-less, so teams can begin with minimal upfront modeling. It supports indexing via keys like a relational database, while still allowing entire documents to be stored in a single field. That combination makes it easier to ship early and extend the table structure as new attributes appear—without redesigning a rigid schema.

How does AppSync reduce the need to run and maintain an API server?

AppSync provides a managed GraphQL layer. Developers define a GraphQL schema and then map incoming queries to data sources. In this setup, a Lambda function is used as the data source behind a resolver, so there’s no need to host a custom server for GraphQL. The result is a GraphQL endpoint that can query DynamoDB through defined resolvers and authorization.

What role does Cognito play, and why is it positioned as the authentication default?

Cognito manages user authentication complexity such as password requirements, account recovery, and sign-in flows. The stack uses Cognito components—user pool, identity pool, and an app client—to support authentication and authorization. Cognito also integrates directly with AppSync, so JWT-based authorization can be wired into GraphQL requests without building login logic from scratch.

How does Pulumi fit into the stack beyond “deploying things”?

Pulumi is used to define AWS infrastructure as code and provision resources like DynamoDB tables, AppSync endpoints and schemas, Lambda resolver wiring (including roles and policies), and Cognito pools. Because everything is codified, the same environment can be recreated quickly for new projects. The walkthrough also uses Pulumi outputs (IDs/endpoints) to configure the frontend automatically.

What’s the division of labor between Amplify and Apollo Client on the frontend?

Amplify handles authentication and provides the connection/configuration to the backend, including generating GraphQL types from the AppSync schema. Apollo Client is then used to execute GraphQL queries and mutations. A custom fetch flow attaches the JWT token to the GraphQL endpoint so AppSync can authorize requests, while Apollo benefits from caching and GraphQL tooling.

Why is the stack described as “serverless,” and how does that affect cost and operations?

The architecture avoids provisioning always-on servers. Cloud functions (Lambda) handle request logic, and managed services (DynamoDB, AppSync, Cognito) remove operational burdens like patching and environment management. Billing aligns with usage: DynamoDB charges per read/write operations, and the compute layer is usage-based—reducing the risk of high fixed costs during early development.

Review Questions

  1. If you needed to add a new GraphQL query that reads additional fields from DynamoDB, which components would you modify (schema, resolvers, frontend types), and why?
  2. How would you ensure authenticated GraphQL requests include the correct JWT token when using Apollo Client with AppSync?
  3. What tradeoffs come from using a schema-less DynamoDB design early, and how does the stack’s approach help manage those tradeoffs?

Key Points

  1. 1

    Use DynamoDB for a schema-less, document-friendly data store that can evolve without heavy upfront modeling.

  2. 2

    Build the API with AppSync by defining a GraphQL schema and mapping resolvers to managed data sources instead of running a custom GraphQL server.

  3. 3

    Rely on Cognito for authentication flows (user pool, identity pool, app client) to avoid building and maintaining login logic.

  4. 4

    Provision everything with Pulumi so DynamoDB, AppSync, Lambda resolver wiring, and Cognito are created repeatably and can be recreated quickly for new SaaS projects.

  5. 5

    Accelerate the frontend with Next.js + Chakra UI, and connect to the backend using Amplify for configuration and auth handling.

  6. 6

    Use Amplify’s schema-driven type generation alongside Apollo Client for GraphQL execution and caching, attaching JWT tokens for authorization.

  7. 7

    Adopt usage-based serverless components to keep early costs low and eliminate ongoing server maintenance work.

Highlights

The stack’s fastest path to a working SaaS is a managed GraphQL API (AppSync) plus managed auth (Cognito) plus infrastructure-as-code (Pulumi), all wired to DynamoDB and Lambda resolvers.
Amplify can generate GraphQL queries/mutations and TypeScript types directly from the AppSync schema, reducing schema drift between frontend and backend.
Authenticated GraphQL requests are handled by passing the JWT token from Amplify into Apollo’s fetch flow so AppSync can authorize queries.

Topics

  • Serverless SaaS
  • AWS AppSync
  • Cognito Authentication
  • Pulumi Infrastructure as Code
  • Next.js Amplify GraphQL

Mentioned