Episode 41 builds a full-stack e-commerce project: a monorepo with Turborepo, a Next.js and Apollo Client frontend, a Node.js backend with Apollo Server and Prisma, a product catalog with search, a shopping cart with mutations, Stripe payment integration, and production considerations.

Starting with episode 41, you enter the real-world projects phase — combining all the skills from the previous 40 episodes into one real application. First project: a complete e-commerce platform.
We'll design a monorepo architecture, build a GraphQL backend with Prisma, implement core features like the product catalog and shopping cart, integrate Stripe payments, and close with production considerations.
This project uses a monorepo — one repository containing many packages:
npx create-turbo@latest ecommerceThe structure we'll build:
ecommerce/
apps/
web/ # Next.js + Apollo Client
api/ # Node.js + Apollo Server + Prisma
packages/
ui/ # shared components
shared/ # shared types and utilitiesThe GraphQL schema is split by domain: Product, Cart, Order, and User.
The Prisma model for products:
model Product {
id Int @id @default(autoincrement())
name String
description String
price Int
category String
stock Int @default(0)
createdAt DateTime @default(now())
}The query with search and filters:
type Query {
products(search: String, category: String, first: Int!, after: String): ProductConnection!
}The resolver combines text search, category filtering, and cursor pagination (episode 12). Add indexes on frequently filtered columns.
The cart is a domain involving intensive mutations:
type Mutation {
addToCart(input: AddToCartInput!): Cart
updateCartItem(input: UpdateCartItemInput!): Cart
removeFromCart(itemId: ID!): Cart
}The cart is stored per user in the database, with stock checks when items are added (resolver validation, episode 10). Each mutation returns the latest cart state so the client can just render it.
Auth uses the pattern from episode 13: login and signup mutations with bcrypt and JWT, with the user attached to the context. Order management uses the result pattern with unions (episode 11) so errors (out of stock, declined card) are handled cleanly.
Payment integration uses the Stripe checkout pattern; install with npm install stripe:
import Stripe from "stripe";
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);
async function createCheckout(_, args, ctx) {
const session = await stripe.checkout.sessions.create({
payment_method_types: ["card"],
line_items: args.input.items.map((item) => ({
price_data: {
currency: "idr",
product_data: { name: item.name },
unit_amount: item.price,
},
quantity: item.quantity,
})),
mode: "payment",
success_url: "https://app.kalian.com/order/success",
cancel_url: "https://app.kalian.com/cart",
});
return { url: session.url };
}A Stripe webhook verifies payment and changes the order status — remember to verify the webhook signature for security.
Real-time order tracking uses subscriptions (episode 16): each order status change is published, and the tracking page listens. Product recommendations can start simple: products in the same category or the most viewed, cached in Redis.
Before production, apply all the best practices:
docker compose up -d --buildThis e-commerce project is a foundation you can extend: add an admin dashboard, analytics, and email notifications once the core architecture runs.
Key takeaways:
In the next episode, episode 42, you'll learn about a social media platform project — user profiles and relationships, post creation with media upload, comments and reactions, following-followers, a paginated feed, real-time notifications, chat, and feed performance strategies at scale. You'll build a complete social app!