Zhivko's Dev Blog

Project write-ups: full-stack engineering, AI, and machine learning — built end-to-end.

View on GitHub
July 18, 2026

I’ve been a developer for more than 10 years, but full-stack work is a recent chapter. For the past two years I’ve been doing React, and more recently Next.js — always learning on the go, picking up just enough of the framework to ship the feature in front of me. That works, but it leaves gaps. You end up knowing how to do things without always knowing why, or what the idiomatic approach would be.

So I decided to close those gaps properly: review the frameworks in depth and build a showcase project end-to-end — not a todo app, but something with real complexity. The result is TechShop: a multi-platform e-commerce application with a Next.js web app, a React Native mobile app, and a shared backend.

Why Now: The Course and the Person Behind It

This project is the capstone for SoftUni’s Full Stack Apps with AI course. But my reason for applying wasn’t primarily the curriculum — it was the instructor.

Svetlin Nakov is the person who got me into IT. I found his free programming courses back in 2012/13, teaching myself in the evenings while he was still at Telerik Academy — and when he founded SoftUni, I joined its first-ever admission in 2014. That path carried me into the software industry — a decision that changed my life. More than a decade later, getting to learn from him directly again, this time on modern full-stack development with AI-assisted workflows, felt like closing a loop. I applied without hesitation.

What I Built

TechShop is an electronics webshop with everything a real one needs:

TechShop storefront

The Stack

Layer Choice
Web + backend Next.js 16 (App Router) · React · TypeScript
Styling Tailwind CSS v4 · shadcn/ui (Base UI)
Database Neon serverless PostgreSQL · Drizzle ORM
Auth Custom JWT (jose + bcryptjs)
Payments Stripe Checkout + webhooks
Files Cloudflare R2 presigned uploads
Mobile Expo · expo-router · TanStack Query
Monorepo pnpm workspaces

Everything deploys for $0/month: Vercel for the web app, Netlify for the Expo web export, Neon’s free tier for the database, Cloudflare R2 for images, Stripe test mode for payments.

Architecture: One Backend, Two Clients

The most instructive design decision was how the two clients talk to the same backend:

apps/web        Next.js app — backend APIs + web client
apps/mobile     Expo app — customer mobile client
packages/shared Zod schemas, DTO types, constants shared by both

The key rule, enforced from day one: all business logic lives in a services layer (product.service.ts, order.service.ts, cart.service.ts, …). Server Actions and REST route handlers are both thin adapters over the same functions. Without this, you inevitably end up with the web and mobile paths drifting apart.

The shared package is the other half of that story — Zod schemas and DTO types used by the web forms, the API routes, and the mobile app. One definition of what a “valid registration” is, validated in three places.

Where My Backend Instincts Needed Updating

The fundamentals weren’t the lesson here — ten years of backend work covers auth, idempotent payment webhooks, and money as integer cents, and all of that transferred unchanged. What needed updating was my mental model of where those concerns live, because Next.js redraws the request/response boundary I was used to.

Rendering strategy is an architectural decision, made per route. On a classic backend, every request hits the server and caching is something you bolt on in front. Next.js inverts that: each page declares how it renders, and the honest answer differs per page type. The home page is static with ISR. Product listings are dynamic Server Components driven by searchParams — filter state lives in the URL, so shareable links come for free — streamed with <Suspense>. Product details get per-product ISR; cart, account, and admin are fully dynamic and auth-gated. Learning on the go, I’d always taken whatever the framework defaulted to. This time every page has a deliberate answer, and I can defend each one.

Product listing with URL-driven filters

Middleware looks like the auth layer — it isn’t. The Express instinct is to authenticate once at the middleware layer and treat everything behind it as safe. Next.js middleware is the wrong place for that: here it only redirects unauthenticated users away from /account and /admin, which is UX, not security. Every Server Action and route handler is its own public entry point, so enforcement is requireUser() / requireAdmin() verifying the JWT inside each one, plus ownership checks at the service level (getOrder filters by owner unless you’re an admin). Defense in depth isn’t news; where the framework forces the layers to sit is. Tokens follow the same logic per platform: httpOnly cookies on the web — never localStorage — and the platform Keychain via expo-secure-store on mobile.

Guest carts were the best small design exercise. Not because carts are hard, but because the merge semantics are where the design actually lives: an anonymous visitor’s cart is a cookie; on login it merges into the server-side cart, which then follows the user across devices — web and mobile share the same cart. Small feature, and it exercises the whole stack: cookies, auth boundary, services layer, both clients.

Search stayed boring on purpose. At 12k rows, plain ILIKE with a pg_trgm GIN index is fast enough and simple to explain, so I resisted reaching for heavier machinery and documented the trade-off instead — keyset pagination and proper full-text search are the named next steps at real scale. Knowing when the boring option is correct, and writing down when it stops being correct, is the part I care about demonstrating.

Working with AI, Deliberately

Since the course is “Full Stack Apps with AI”, the process matters as much as the product. The repo has an AGENTS.md — operational rules for AI coding agents: business logic only in services, schema changes only via committed Drizzle migrations, money in cents, every list endpoint paginated, pnpm lint && pnpm typecheck before every commit.

Writing constraints down changed how the AI collaboration felt. Instead of reviewing every generated line for architectural drift, the agent works within guardrails and I review for correctness. The commit history — 42 focused, conventional commits over eight days — reads like a deliberate implementation plan being executed, because that’s what it is: I wrote a full spec before the first line of code, with milestones mapped to individual commits.

Where It Ended Up

Everything described above shipped. The web app — storefront, search, reviews, cart, Stripe checkout, order history, and the whole admin panel with R2 image uploads — is live on Vercel. The mobile app covers the full customer journey and is deployed as an Expo web export on Netlify. Both run against the same backend and database.

   
Code github.com/zhivko-georgiev/techshop
Web app techshop-zhivko.vercel.app
Mobile (Expo web) techshop-zhivko.netlify.app

You can try it with the demo accounts — demo@techshop.dev / Demo123! for the customer experience, or admin@techshop.dev / Admin123! to poke around the admin panel. Checkout runs in Stripe test mode (card 4242 4242 4242 4242), so feel free to buy things.

TechShop admin panel — products

Two items from the original plan didn’t make the capstone cut: CI with GitHub Actions and a test suite with Vitest and Playwright. They’re the natural next iteration, and I’d rather say that plainly than pretend the project has coverage it doesn’t.

More than a decade in, it turns out the best way to fill the gaps of learning on the go is still the same as it was in 2012: find a good teacher, pick a real project, and build the whole thing.