parpkin logo

Building Parpkin, Week 1: The Foundation Before The Food

How Parpkin’s first real milestone became branch scope, auth, typed APIs, and operational foundations before a polished ordering experience.restaurant-software

ByNana Gaisie
5 mins
ParpkinProduct EngineeringTanstack StartRestaurant Software

Building Parpkin, Week 1: The Foundation Before The Food

The tempting version of this story starts with the food.

A shawarma ordering app should open with a menu, a cart, a checkout button, and enough polish to make someone hungry. That is the visible part. It is also the part most people judge first.

But the first meaningful work on Parpkin was less glamorous: auth, branches, organizations, typed APIs, database shape, onboarding, maps, and route boundaries. Before a customer can order from a restaurant, the system has to know which restaurant branch they are ordering from. Before staff can manage an order, the system has to know which staff member is allowed to see which branch. Before a menu can look beautiful, it has to belong somewhere.

So the foundation of Parpkin became branch scope.

Working thesis: Parpkin is not just an ordering interface. It is becoming a restaurant operating system for Parpkin Shawarma: customers discover a branch and order food, while staff manage menus, orders, payments, analytics, customers, loyalty, promotions, and settings from the restaurant portal.

What I Started With

The first tracked commit in this build was a TanStack Start scaffold on April 27, 2026. It already carried more ambition than a simple frontend experiment:

  • TanStack Start, React, TypeScript, Vite, and Tailwind
  • shadcn-style UI components
  • Better Auth
  • Drizzle and Postgres
  • oRPC
  • Sentry instrumentation
  • early route structure
  • public assets and app metadata

That set the tone. This was going to be a full-stack product, not a static page wrapped around a menu.

The first few days were about making the app capable of knowing who is using it and what operational context they are in. That meant owner registration, onboarding, organization creation, branch creation, and session middleware.

2026-04-27  Add initial TanStack Start scaffold
2026-04-28  Add auth flows, UI components, and DB schema
2026-04-28  Add owner registration flow
2026-04-28  Create Parpkin org on owner signup
2026-04-28  Add onboarding and app session middlewares
2026-04-28  Add initial branch creation endpoint

That sequence says a lot. The first product question was not “how do we render shawarma cards?” It was “how do we represent a restaurant operation?”

The Core Decision: Branches Are Not Metadata

Restaurants are physical. A branch has a location, hours, staff, menus, availability, incoming orders, payment settings, and operational constraints. Treating that as a decorative field would make every later feature harder.

So Parpkin uses Better Auth organizations and teams as the foundation for restaurant and branch scope. The session carries active organization and active team context, and feature code derives scope from that instead of asking the client to declare what branch it wants to mutate.

That decision now appears throughout the codebase:

  • menu management is branch-scoped
  • menu image uploads are authorized against the active branch
  • customer carts are scoped to the selected branch
  • staff orders read and update orders for the active branch
  • analytics resolve around branch and organization access
  • settings split between branch-level and restaurant-level data
  • staff roles and invitations account for branch assignment

The important part is not the specific library choice. It is that tenancy and branch context became a first-class product constraint.

{/* TODO: Add system diagram showing customer portal, restaurant portal, oRPC, auth, and database boundaries. */}

The Shape Of The App

The repository settled into a pattern that made the product easier to extend:

src/routes/**       route wiring
src/features/**     feature logic, components, hooks, server helpers
src/orpc/**         typed contracts, schemas, routers, client
src/db/**           Drizzle schema and database access
src/integrations/** maps, payments, email, analytics, themes

That separation matters because Parpkin is already split across very different user surfaces:

  • _auth for registration, sign-in, reset, and onboarding
  • _authed for the restaurant portal
  • _customer_portal for customer ordering and history
  • public routes for homepage, about, contact, privacy, sitemap, and robots

The route files mostly wire pages into TanStack Router. The actual work lives in feature modules. That makes it possible to grow the restaurant portal without turning route files into product logic dumps.

Here is the kind of domain map the API router now exposes:

const router = os.router({
  analytics: { ... },
  branch: { ... },
  customerCart: { ... },
  customerPayments: { ... },
  customers: { ... },
  customerOrders: { ... },
  loyalty: { ... },
  menu: { ... },
  promotions: { ... },
  settings: { ... },
  staff: { ... },
  staffOrders: { ... },
  upload: { ... },
});

That is the real foundation. Once these boundaries exist, adding a feature becomes less about inventing a new place for code and more about fitting the feature into the right product domain.

Maps Came Early For A Reason

The next foundational layer was location.

On May 1 and May 2, the project moved through Google Maps, Mapbox, a provider abstraction, and branch-aware dashboard/customer map flows. That may sound like a UI detail, but in this product it connects directly to the core model.

A customer does not order from “Parpkin” in the abstract. They order from a branch. A staff member does not manage every operational queue by default. They operate in a branch context.

So the customer branch picker persists selection locally, while the authenticated dashboard uses active session branch context. Two surfaces, same product idea.

2026-05-01  Replace Google Maps with Mapbox integration
2026-05-02  Add Mapbox search and map integration
2026-05-02  Add dashboard UI, hooks, and map integration
2026-05-02  Add map provider abstraction and adapters

Later, when HERE and Mapbox browser SDKs created SSR and CSP issues, the fix was not to abandon maps. The fix was to respect the boundary: browser-only code needs to load like browser-only code, while the public site still needs crawler-visible HTML.

Menu Management Was The First Big Domain

Once auth, onboarding, branches, and maps existed, menu management became the first major restaurant workflow.

The plan was explicit: replace insufficient organization-scoped menu structures with branch-scoped menus. Items, categories, modifiers, and availability needed to belong to a branch and obey branch-aware permissions.

That led to:

  • a menu feature module
  • oRPC menu handlers
  • dashboard menu routes
  • category and item editor sheets
  • modifier groups
  • availability handling
  • seed scripts
  • typed schemas and form defaults

This was the first place where the product pattern really hardened:

  • Put domain UI and hooks under src/features/menu.
  • Put API behavior under src/orpc/router/menu.ts.
  • Put database shape under src/db/schema.ts.
  • Keep route files thin.
  • Derive branch scope on the server.

The benefit showed up immediately in the customer ordering work that followed. The customer-facing menu could consume the same branch-scoped menu data staff manage in the portal. There was no need to invent a second “public menu” system.

Food Needed Images

A restaurant menu without images can work, but it does not sell as well.

The image work started right after menu management. The app added Cloudflare R2 support, S3-compatible helpers, pre-signed upload URLs, direct browser uploads, progress reporting, and a component for rendering persisted R2 images.

The decision here was practical: do not proxy large image uploads through the app server. Let the backend issue a short-lived upload URL after checking permissions, then let the browser upload directly to object storage.

// The shape of the decision, not the full implementation:
// 1. Authenticated staff asks for an upload URL.
// 2. Server derives active branch scope from the session.
// 3. Server validates file metadata and permissions.
// 4. Browser uploads directly to R2 with progress events.
// 5. Menu item stores the resulting public object URL.

That one feature connected infrastructure, permissions, UX, and food presentation. It also created a reusable rule that still matters: persisted R2 URLs render through the R2 image component, while temporary client-side previews stay as plain images.

From Foundation To Product Loop

By May 8 and May 9, the foundation was strong enough to support the first customer ordering loop:

  • browse a branch menu
  • configure modifiers
  • add to cart
  • sync cart state
  • checkout
  • create orders
  • support anonymous customer sessions

This is when the visible app started catching up to the underlying architecture.

But the reason it could move quickly is that the earlier decisions had already created the right constraints. Branch scope existed. Menus were structured. The API boundary was typed. The customer portal had its own route group. The database had somewhere for orders to live.

The product did not emerge from one big “build checkout” moment. It emerged from a set of foundations that made checkout possible.

{/* TODO: Add commit timeline for 2026-04-27 through 2026-05-09 foundation milestones. */}

What This Foundation Enables

The work after this first foundation moved fast:

  • staff orders and operational queues
  • staff invitations and role management
  • analytics for sales, orders, and top items
  • customer profiles, loyalty, rewards, and promotions
  • payment attempts, public order numbers, and resumable checkout
  • manual payment confirmation
  • customer history and branch discovery
  • public pages, SEO, sitemap, robots, and QR assets

That list is broad, but it is not random. Each feature attaches to one of the foundational primitives:

  • branches
  • sessions
  • typed APIs
  • feature boundaries
  • database-backed operational records
  • customer/staff route separation

This is the lesson I am taking from the first phase of Parpkin: if the domain model is wrong, every feature feels like a workaround. If the domain model is strong enough, product work starts to compound.

What I Would Show In This Post

Illustrations and demos to add later:

  • a system diagram showing customer portal, restaurant portal, oRPC, auth, and database boundaries
  • a branch-scope diagram showing organization -> branch/team -> menus/orders/staff/settings
  • a screenshot of the owner onboarding and branch creation flow
  • a screenshot of the menu management area
  • a short interactive demo that switches between customer branch selection and staff active branch context
  • a code snippet from the oRPC router domain map

Where The Next Installment Goes

The next installment should focus on menu management.

That is the first domain where the architecture becomes tangible: categories, items, modifiers, availability, images, branch permissions, and the bridge between what staff configure and what customers can order.

The foundation was not the food. It was everything the food needed before it could become a real ordering experience.