404 Not Found

404 Not Found


nginx

Project Development

Once the design was complete, Charlie led the team in starting to code. The authentication module went live first—no login, no access. Next came the product module—CRUD operations and ISR caching for millions of products. The shopping cart and order modules followed closely behind. Alice tested the customer flow, while Bob verified the administrative functions; each module underwent end-to-end testing.

1. What You'll Learn


2. A True Story About a Team

(1) Pain Point: Design Plans Can't Be Implemented

Charlie has a complete set of design documents, but the team doesn't know what to work on first and what to do next. Alice wrote the product pages, but the shopping cart doesn't have an API yet; Bob wrote the admin panel, but authentication isn't set up yet. The modules are waiting on each other, and progress has stalled.

(2) A Solution for Incremental Development by Module

Develop each module in order of dependency: Authentication → Products → Shopping Cart → Orders → Internationalization. Test each module immediately upon completion, without relying on modules that are not yet finished.

(3) Results: Steady Progress

Each module takes 3-5 days to complete, and all core features will be launched within two weeks, at which point Alice will be able to complete the entire shopping process.


3. Development Milestones

(1) Module Development Gantt Chart

100%
gantt
    title MegaShop Development Milestones
    dateFormat YYYY-MM-DD
    section Auth Module
    JWT + OAuth2 :a1, 2026-01-01, 3d
    Auth Middleware :a2, after a1, 2d
    section Product Module
    CRUD API :p1, after a2, 3d
    Search + Filter :p2, after p1, 2d
    ISR Cache :p3, after p2, 1d
    section Cart Module
    Store + API :c1, after a2, 3d
    Composable :c2, after c1, 1d
    section Order Module
    Prisma Transaction :o1, after c1, 3d
    Stripe Payment :o2, after o1, 2d
    section i18n + Perf
    Three Languages :i1, after p3, 2d
    Image Optimization :i2, after i1, 1d
    Lighthouse Audit :i3, after i2, 1d

4. Authentication Module

(1) ▶ Example: JWT Tool + Registration/Login API

TYPESCRIPT
// server/utils/jwt.ts
import jwt from 'jsonwebtoken'

const config = useRuntimeConfig()

export function signTokenPair(payload: { userId: number; email: string; role: string }) {
  return {
    accessToken: jwt.sign(payload, config.jwtAccessSecret, { expiresIn: '15m' }),
    refreshToken: jwt.sign(payload, config.jwtRefreshSecret, { expiresIn: '7d' })
  }
}

export function setAuthCookies(event: any, tokens: { accessToken: string; refreshToken: string }) {
  setCookie(event, 'access-token', tokens.accessToken, { httpOnly: true, secure: true, sameSite: 'lax', maxAge: 900 })
  setCookie(event, 'refresh-token', tokens.refreshToken, { httpOnly: true, secure: true, sameSite: 'lax', maxAge: 604800 })
}

Output:

TEXT
// Execution Successful
TYPESCRIPT
// server/api/auth/register.post.ts
export default defineEventHandler(async (event) => {
  const { email, password, name } = await readBody(event)
  const existing = await prisma.user.findUnique({ where: { email } })
  if (existing) throw createError({ statusCode: 409, message: 'Email already registered' })

  const hashedPassword = await hashPassword(password)
  const user = await prisma.user.create({
    data: { email, name, password: hashedPassword, role: 'CUSTOMER' }
  })

  const tokens = signTokenPair({ userId: user.id, email: user.email, role: user.role })
  setAuthCookies(event, tokens)

  return { user: { id: user.id, email: user.email, name: user.name, role: user.role } }
})

5. Product Module

(1) ▶ Example: Product CRUD API (Prisma + Cache)

TYPESCRIPT
// server/api/products/index.get.ts
export default cachedEventHandler(
  async (event) => {
    const { page = '1', limit = '20', category, search, sort } = getQuery(event)

    const where = {
      AND: [
        category ? { category: { slug: category as string } } : {},
        search ? { OR: [
          { name: { contains: search as string, mode: 'insensitive' } },
          { description: { contains: search as string, mode: 'insensitive' } }
        ]} : {}
      ]
    }

    const orderBy = sort === 'price-asc' ? { price: 'asc' }
      : sort === 'price-desc' ? { price: 'desc' }
      : { createdAt: 'desc' }

    const [items, total] = await Promise.all([
      prisma.product.findMany({
        where, orderBy,
        skip: (Number(page) - 1) * Number(limit),
        take: Number(limit),
        include: { category: { select: { name: true, slug: true } } }
      }),
      prisma.product.count({ where })
    ])

    return { items, total, page: Number(page), limit: Number(limit) }
  },
  { maxAge: 60, swr: true, getKey: (event) => `products:${getRequestURL(event).search}` }
)

Output:

TEXT
// Execution Successful

(2) ▶ Example: Product Details + SEO

VUE
<!-- pages/products/[id].vue -->
<template>
  <div v-if="product">
    <h1>{{ product.name }}</h1>
    <p>{{ formatFromUSD(product.price) }}</p>
    <button @click="handleAddToCart">{{ t('product.addToCart') }}</button>
  </div>
</template>

<script setup lang="ts">
const route = useRoute()
const config = useRuntimeConfig()
const { t } = useI18n()
const { formatFromUSD } = useLocalizedPrice()
const { addToCart } = useCart()

const { data: product } = await useFetch(`/api/products/${route.params.id}`)

// Dynamic SEO per product
useSeoMeta({
  title: () => `${product.value?.name} - MegaShop`,
  description: () => `Buy ${product.value?.name} - $${product.value?.price} USD`,
  ogTitle: () => product.value?.name,
  ogImage: () => product.value?.image,
  ogType: 'product'
})

// JSON-LD structured data
useHead({ script: [{ type: 'application/ld+json', innerHTML: computed(() => JSON.stringify({
  '@context': 'https://schema.org', '@type': 'Product',
  name: product.value?.name,
  offers: { '@type': 'Offer', priceCurrency: 'USD', price: product.value?.price }
}))}]})

async function handleAddToCart() {
  if (product.value) await addToCart(product.value.id, product.value.name)
}
</script>

Output:

TEXT
// Execution Successful

6. Shopping Cart Module

(1) ▶ Example: cartStore + useCart

TYPESCRIPT
// stores/cart.ts
export const useCartStore = defineStore('cart', () => {
  const items = ref<CartItem[]>([])
  const totalItems = computed(() => items.value.reduce((s, i) => s + i.quantity, 0))
  const totalPrice = computed(() => items.value.reduce((s, i) => s + i.price * i.quantity, 0))

  async function addItem(productId: number) {
    const cart = await $fetch('/api/cart/add', { method: 'POST', body: { productId } })
    items.value = cart.items
  }
  async function removeItem(productId: number) {
    const cart = await $fetch('/api/cart/remove', { method: 'DELETE', body: { productId } })
    items.value = cart.items
  }
  async function fetchCart() {
    const cart = await $fetch('/api/cart')
    items.value = cart.items || []
  }

  return { items, totalItems, totalPrice, addItem, removeItem, fetchCart }
})

Output:

TEXT
// Execution Successful

(2) ▶ Example: Shopping Cart API (with Inventory Validation)

TYPESCRIPT
// server/api/cart/add.post.ts
export default defineEventHandler(async (event) => {
  const userId = event.context.user?.id
  const { productId, quantity = 1 } = await readBody(event)

  // Check stock
  const product = await prisma.product.findUnique({ where: { id: productId } })
  if (!product?.inStock) throw createError({ statusCode: 400, message: 'Product out of stock' })

  // Upsert cart item
  await prisma.cartItem.upsert({
    where: { userId_productId: { userId, productId } },
    update: { quantity: { increment: quantity } },
    create: { userId, productId, quantity }
  })

  const cart = await prisma.cartItem.findMany({
    where: { userId },
    include: { product: { select: { name: true, price: true, image: true } } }
  })

  const total = cart.reduce((s, i) => s + Number(i.product.price) * i.quantity, 0)
  return { items: cart, total }
})

Output:

TEXT
// Execution Successful

7. Order Module

(1) ▶ Example: Order Creation Transaction

TYPESCRIPT
// server/api/orders/index.post.ts
export default defineEventHandler(async (event) => {
  const userId = event.context.user?.id
  if (!userId) throw createError({ statusCode: 401, message: 'Login required' })

  const order = await prisma.$transaction(async (tx) => {
    // Get cart items
    const cartItems = await tx.cartItem.findMany({
      where: { userId },
      include: { product: true }
    })

    if (cartItems.length === 0) {
      throw createError({ statusCode: 400, message: 'Cart is empty' })
    }

    // Check stock for all items
    for (const item of cartItems) {
      if (!item.product.inStock) {
        throw createError({ statusCode: 400, message: `${item.product.name} out of stock` })
      }
    }

    // Calculate total
    const total = cartItems.reduce((sum, item) => sum + Number(item.product.price) * item.quantity, 0)

    // Create order with items
    const newOrder = await tx.order.create({
      data: {
        userId,
        total,
        status: 'PENDING',
        items: {
          create: cartItems.map(item => ({
            productId: item.productId,
            quantity: item.quantity,
            price: item.product.price
          }))
        }
      },
      include: { items: { include: { product: true } } }
    })

    // Clear cart
    await tx.cartItem.deleteMany({ where: { userId } })

    return newOrder
  })

  return { order, message: 'Order created' }
})

Output:

TEXT
// Execution Successful

8. Internationalization and Performance

(1) ▶ Example: Complete production configuration in nuxt.config.ts

TYPESCRIPT
// nuxt.config.ts
export default defineNuxtConfig({
  ssr: true,

  modules: [
    '@pinia/nuxt',
    '@nuxtjs/tailwindcss',
    '@nuxtjs/i18n',
    '@nuxtjs/sitemap',
    '@nuxt/image',
    '~/modules/analytics'
  ],

  app: {
    head: {
      title: 'MegaShop',
      titleTemplate: '%s | MegaShop',
      meta: [
        { name: 'description', content: 'Over 1 million products shipped worldwide' }
      ]
    }
  },

  i18n: {
    locales: [
      { code: 'en', name: 'English', file: 'en.json', currency: 'USD' },
      { code: 'zh', name: 'Chinese', file: 'zh.json', currency: 'CNY' },
      { code: 'ja', name: 'Japanese', file: 'ja.json', currency: 'JPY' }
    ],
    defaultLocale: 'en',
    lazy: true,
    langDir: 'locales/',
    strategy: 'prefix_except_default'
  },

  image: {
    quality: 80,
    format: ['webp', 'avif']
  },

  sitemap: {
    hostname: 'https://megashop.com',
    gzip: true
  },

  runtimeConfig: {
    databaseUrl: process.env.DATABASE_URL,
    redisUrl: process.env.REDIS_URL,
    jwtAccessSecret: process.env.JWT_ACCESS_SECRET,
    jwtRefreshSecret: process.env.JWT_REFRESH_SECRET,
    public: {
      apiBase: process.env.API_BASE || '/api',
      siteUrl: process.env.SITE_URL || 'https://megashop.com',
      stripePublishableKey: process.env.STRIPE_PUBLISHABLE_KEY
    }
  },

  routeRules: {
    '/': { prerender: true },
    '/about': { prerender: true },
    '/products': { swr: 3600 },
    '/products/**': { swr: 86400 },
    '/categories/**': { swr: 21600 },
    '/admin/**': { ssr: false },
    '/cart': { ssr: false },
    '/checkout': { ssr: false },
    '/profile/**': { ssr: false },
    '/api/**': { cors: true },
    '/_nuxt/**': { headers: { 'cache-control': 'public, max-age=31536000, immutable' } }
  },

  nitro: {
    preset: process.env.DEPLOY_TARGET || 'node-server',
    storage: {
      products: { driver: process.env.NODE_ENV === 'production' ? 'redis' : 'memory' }
    }
  },

  components: [{ path: '~/components', pathPrefix: false }]
})

Output:

TEXT
// Execution Successful

❓ FAQ

Q Is the order in which modules are developed important?
A It's very important. Authentication must be implemented first (since other modules depend on the logged-in state), followed by products (since the shopping cart and orders depend on product data), and finally internationalization (since it doesn't affect core functionality).
Q How much code does each module require, roughly?
A Authentication module ~500 lines, Product module ~800 lines, Shopping Cart ~400 lines, Orders ~500 lines, Internationalization ~200 lines. Including configuration and tests, the total is approximately 3,000-4,000 lines.
Q How do we ensure API contracts between modules?
A Use TypeScript interfaces to define API request and response types, and place them in shared/types.ts. The frontend and backend share these type definitions, and consistency is checked at compile time.
Q How do I transition from mock data to Prisma during development?
A Keep the API interface format unchanged; only replace the internal implementation. Switch from a mock array to prisma.findMany(); the route and response formats remain the same.
Q How do I test Stripe payments?
A Use a Stripe test secret and a test card number (4242 4242 4242 4242). No actual charges will ever be processed. Switch to the production secret before going live.
Q When should i18n translation be done?
A i18n should be done after the core features are complete. First, hard-code the English text; once the features are stable, extract them as translation keys. Starting i18n too early will increase development complexity.

📖 Summary


📝 Exercises

  1. Basic Exercise (Difficulty: ⭐): Implement an authentication module (registration/login/refresh token), and verify that the cookie is set correctly
  2. Advanced Exercise (Difficulty: ⭐⭐): Implement the complete product CRUD workflow, including the shopping cart and checkout, as a fully functional end-to-end application.
  3. Challenge (Difficulty: ⭐⭐⭐): Complete all core modules of MegaShop + i18n + performance optimization, achieving a Lighthouse score of 90+

---|

Web-Tutorial.com

Web-Tutorial Tech Team

A team of developers maintaining programming tutorials. Each tutorial is written and reviewed by developers with expertise in that field. We work to keep our content accurate and reliable — if you spot an issue, please let us know.

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏