← Back to Projects

Engineering Case Study

Peste Peste

A production-oriented full-stack e-commerce application built with Next.js, focused on reliable checkout, secure authentication, server-side business logic, and consistent product data.

Engineering Decisions

Key architectural and technical decisions made during the development of the project.

01

Server-first application architecture

The application follows a server-first architecture where database access and sensitive business logic remain on the server. Server Actions handle administrative CRUD operations, while API routes are used for authentication and payment-related workflows.

02

Secure authentication and authorization

Authentication is built around short-lived access tokens and refresh tokens. Refresh tokens are stored in HttpOnly cookies, while user roles are used for authorization and protecting admin-only routes and operations.

03

Server-authoritative business logic

Critical business logic such as product pricing, inventory validation, and order totals is handled on the server. Client-side values are treated as untrusted input rather than the source of truth for transactional data.

04

Consistent client-side state management

Redux Toolkit manages cart state across the application, while LocalStorage persists cart data between sessions. Server-side validation is used to reconcile persisted client state with the latest database values when necessary.

05

Data-aware caching strategy

Caching and revalidation are applied according to data volatility. Relatively stable data can benefit from caching, while frequently changing transactional data such as prices and inventory is validated against the server before critical operations.

06

URL-driven product discovery

Search, filtering, sorting, and pagination state are represented through URL search parameters. This makes product discovery shareable, refreshable, and compatible with browser navigation without relying entirely on local component state.

Engineering Challenges

Real implementation challenges and the approaches used to solve them.

01

Preventing checkout with stale cart prices

Problem

Product prices could change after an item was added to the cart, leaving the client with outdated pricing data that could lead to an incorrect order total.

Solution

A server-side validation flow compares cart prices against the latest database values before checkout. When a price changes, the cart is synchronized across Redux and LocalStorage, and the user cannot continue with stale pricing. The final order total is always calculated from current server-side product data.

02

Building a reliable checkout and payment flow

Problem

Checkout involved multiple dependent steps including price calculation, shipping rules, order creation, payment initiation, and payment verification. Trusting client-side values across these steps could result in inconsistent orders or payment amounts.

Solution

The checkout flow was designed around server-side calculations and validation. Orders are created with database-derived totals, payment authorities are associated with orders, and the payment callback is verified server-side before the order status is updated.

03

Maintaining consistency between payment, orders, and inventory

Problem

A successful payment could require several related state changes, including payment confirmation, order status updates, and inventory reduction. A partial failure could leave these records inconsistent.

Solution

Prisma transactions are used during the payment verification flow so related database changes are committed atomically. This prevents partially completed payment workflows from leaving inconsistent order or inventory state.

04

Moving weight-based pricing to the backend

Problem

Products are sold by weight, meaning the final price depends on the selected package size while inventory is tracked in grams. Keeping this business logic primarily on the client could lead to inconsistent pricing between the UI and the final order.

Solution

The product model uses price_per_kg and stock_gram as the core pricing and inventory values. A backend pricing service calculates the final price for supported weights, including the 250g, 500g, and 1kg pricing rules, and the final order price is recalculated from database data.

05

Building a stable image processing pipeline

Problem

Image uploads to Supabase Storage encountered SharedArrayBuffer compatibility issues, while large source images also increased storage and delivery costs.

Solution

A reusable image-processing pipeline was introduced with Sharp to validate, compress, and convert uploaded images to WebP before storage. Safe Buffer conversion was added to stabilize uploads, and the same pipeline was applied to both product and blog images.

06

Balancing caching with data freshness

Problem

Caching improves performance, but stale cached product data can become problematic in an e-commerce system where prices and inventory may change frequently.

Solution

Caching was applied based on data volatility rather than uniformly across the application. Stable data can benefit from revalidation and caching, while critical transactional values such as prices are revalidated against the server before checkout and payment.