← Back to Projects

Engineering Case Study

Piano Coffee

A full-stack coffee menu management application built with Next.js, focused on role-based access control, server-side data operations, and a clear separation between public menu consumption and administrative management.

Engineering Decisions

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

01

Separating authentication from authorization

Authentication is handled through Supabase Auth, while administrative access is determined separately through the user's role stored in the database. This keeps user identity and application permissions as distinct concerns.

02

Using Server Actions for menu mutations

Menu creation, updates, and deletion are implemented with Next.js Server Actions instead of separate API endpoints for each CRUD operation. This keeps internal menu mutations close to the server-side application logic without introducing unnecessary API endpoints.

03

Enforcing authorization at the server boundary

Administrative permissions are verified on the server before menu mutations are executed. The application does not rely solely on hiding dashboard functionality in the client, keeping authorization independent from the UI.

04

Keeping authentication isolated behind an API route

Authentication uses a dedicated API route because it has its own request and session lifecycle, while internal menu management remains implemented through Server Actions. This keeps the communication pattern appropriate to each responsibility.

05

Separating public and administrative data flows

The public interface focuses on consuming and presenting menu data, while authenticated administrative workflows handle menu mutations. Separating these responsibilities keeps the public experience independent from management operations.

Engineering Challenges

Real implementation challenges and the approaches used to solve them.

01

Protecting administrative menu mutations

Problem

The application exposes a public coffee menu alongside administrative functionality for creating, updating, and deleting menu items. Authenticated users without the required role should not be able to perform these operations.

Solution

Authorization is enforced inside the server-side mutation flow. Before a Server Action performs a database mutation, the authenticated user's role is verified, preventing unauthorized users from modifying menu data.

02

Avoiding client-side authorization as a security boundary

Problem

Hiding administrative UI elements does not prevent a user from attempting to invoke protected operations directly. Client-side visibility therefore cannot be treated as the actual security boundary.

Solution

Authorization checks are performed on the server before administrative operations access the database. The client controls the user experience, while the server remains responsible for enforcing access rules.

03

Choosing between API Routes and Server Actions

Problem

The application required server-side authentication as well as internal menu management mutations. Using API routes for every operation would create endpoints that are only consumed by the same Next.js application.

Solution

API routes are limited to the authentication flow, while menu CRUD operations use Server Actions. This keeps the architecture smaller and matches the communication mechanism to the responsibility of each feature.

04

Keeping public menu data separate from administrative operations

Problem

The public menu and administrative dashboard work with the same underlying menu data but have fundamentally different responsibilities. Mixing their data flows could make access control and maintenance harder to reason about.

Solution

Public menu consumption and administrative mutations are separated at the application level. Public users consume menu data through the storefront, while protected Server Actions handle management operations after authorization checks.