The Client
Gressenteret is a turf and garden supplies business based in Bærum, Norway. They have been delivering quality turf across Oslo, Asker, Bærum and Buskerud since the early 90s. They hold the highest credit rating on Experian. Their product range spans ready-laid turf, green walls, garden accessories, and professional landscaping advice.
The challenge
Gressenteret needed a production-ready e-commerce platform. Not a template. Not a Shopify store. A custom-built system where customers could browse products, calculate turf pricing, and pay with Vipps or Klarna – the two payment methods that matter in Norway. The site needed to handle product images via Cloudflare R2, manage orders through an admin API, and run in Docker for straightforward deployment.
What we built
Full-stack monorepo
The project is structured as a pnpm workspace monorepo with three packages: a Next.js 15 frontend (React 19), a Fastify API backend with Prisma ORM, and a shared package containing Zod validation schemas and TypeScript types used by both apps. This keeps the contract between frontend and backend in sync without duplicating code.
Product catalogue and cart
The storefront includes category pages for turf, green walls, and accessories, each with product detail pages. A client-side cart persists across sessions and feeds into the checkout flow. Product data is managed through the API with admin-key-protected endpoints for creating and updating listings.
Norwegian payment integration
The checkout supports both Vipps and Klarna. The architecture is built with proper webhook handlers for payment confirmation, order status updates, and error recovery. Payment stubs are in place for the test environment, with a clean upgrade path to production API keys.
Image handling with Cloudflare R2
Product images are uploaded via presigned URLs to Cloudflare R2, served through a custom domain. This keeps image storage out of the application server and leverages Cloudflare's edge network for fast delivery across Norway.
Docker-first deployment
The entire stack – PostgreSQL, API, and frontend – runs in Docker Compose. Development and production share the same container architecture with separate compose files. Hot reload is mapped via volume mounts in development. The production config adds health checks and restart policies.
Decisions Worth Explaining
Architecture
Custom over Shopify
Gressenteret's pricing model involves area calculations, delivery zones, and seasonal availability. A platform like Shopify would have needed plugins on top of plugins. Building custom meant every feature fits the actual business logic.
Payments
Vipps + Klarna, not Stripe
In Norway, Vipps is the default mobile payment. Klarna handles invoice and installment purchases. Stripe is popular with developers but not with Norwegian consumers buying garden supplies. We went with what the customers actually use.
Monorepo
Shared types, single source of truth
The shared package means product schemas, order types, and validation rules are defined once and used by both the API and the frontend. No drift between what the API expects and what the frontend sends.
Images
R2 over local storage
Product images change frequently – seasonal items, new stock photos, updated thumbnails. Cloudflare R2 with presigned uploads means the admin can update images without touching the server or redeploying. Edge caching handles the performance.
Tech Stack
- Frontend: Next.js 15.1, React 19, App Router
- Backend: Fastify, Prisma ORM
- Database: PostgreSQL 16
- Payments: Vipps ePayment, Klarna Checkout
- Images: Cloudflare R2
- Validation: Zod (shared package)
- Infrastructure: Docker Compose, Hetzner Cloud
- Monorepo: pnpm workspaces
The result
Gressenteret has a production-ready e-commerce platform that matches how their business actually works. Customers can browse products by category, calculate turf pricing for their area, add items to a persistent cart, and check out with the payment methods they already use. The admin manages products and orders through a clean API. The whole thing deploys with a single docker-compose command.
Not a template store. A platform built for the business.
Custom pricing logic. Norwegian payment methods. Edge-served images. One command to deploy.