Project Metadata
Overview of the project context, scope, and tech choices.
Overview
- Role
- Full Stack Developer
- Client
- Company Owner
- Type
- Internal Business Application
- Industry
- Property Management
- Platform
- Web
- Duration
- -
Description
A multi-property boarding house management platform for handling tenants, rental payments, room management, and operational workflows in a centralized dashboard.
Key Features
- • Multi-property Listings
- • Tenant Management
- • Room Inventory
- • Property Search
- • Booking Flow
- • Rental Payments
Tech Stack
This project is a web platform for managing multiple boarding house properties, combining public property discovery and booking with internal tools for managing rooms, tenants, rentals, and payments.
The platform serves both prospective tenants and property owners, providing a public experience for discovering available rooms and an internal workspace for managing day-to-day rental operations.
Background & Problem Statement
The boarding house business previously relied on scattered records and manual processes to manage property information, room availability, tenant data, and rental payments. As multiple properties and rooms are managed simultaneously, these disconnected workflows can make it difficult to maintain an accurate view of occupancy, rental status, and outstanding payments.
The platform was built to centralize these operational activities into one system, covering:
- Managing multiple properties and room inventories
- Tracking tenants and rental status
- Handling room bookings and rental payments
- Monitoring day-to-day operations through a centralized dashboard
Architecture & Technical Decisions
The application was built with Next.js App Router as the foundation of the web application. The routing structure separates public-facing pages, property owner operations, tenant workflows, and backend API endpoints into distinct application areas.
/app /(site) → Public-facing pages /[kosSlug] → Property pages /[roomSlug] → Room details /search → Property and room search
/owner → Property owner workspace /[kosId] → Active property workspace /onboarding → Owner onboarding
/tenant → Tenant workspace /payments → Payment information /rentals → Rental information /saved → Saved properties or rooms
/api → Backend API Route HandlersThe (site) route group contains the public-facing property experience. Each property is identified by a dynamic kosSlug, while individual rooms use a nested roomSlug.
The owner workspace is scoped to the currently active property. The dynamic kosId identifies the kos/property selected in the owner dashboard, allowing the same workspace structure to be used across different properties.
The tenant workspace does not use a dynamic tenant ID in the URL. Tenant-specific pages are accessed through the authenticated tenant context, with routes such as /tenant/payments, /tenant/rentals, and /tenant/saved. Authentication and session information determine the currently authenticated tenant.
For backend operations, the application uses Next.js Route Handlers under /app/api rather than Server Actions. The API is organized by business domain and handles operations related to owners, rooms, tenants, payments, users, authentication, and addresses.
Client-side components communicate with these API endpoints through standard HTTP requests for data fetching and mutations, including operations such as managing tenants, rooms, property data, and payment-related records.
This structure keeps the public experience, property-scoped owner workspace, authenticated tenant workspace, and backend API layer clearly separated while maintaining the platform within a single Next.js application.
Data Management with Prisma & PostgreSQL
The database schema was designed around the core business entities: properties, rooms, tenants, bookings, rentals, and payments. Prisma ORM provides type-safe access to PostgreSQL, helping keep relationships and data access consistent across the application.
A property can contain multiple rooms, while rooms can have booking and rental activity over time. Tenant and payment records are connected to the rental lifecycle so that operational information can be managed as related data rather than isolated records.
Room availability is derived from the current booking and rental records rather than maintained as a separate value. This allows the same operational state to be used by both the public booking experience and the property management views.
Dashboard queries are structured around the data required by each view, reducing unnecessary database requests and allowing information such as room availability, occupancy, active rentals, and outstanding payments to be retrieved efficiently.
For property discovery, filtering and search are handled directly through PostgreSQL queries without introducing additional search infrastructure.
Payment Integration
The platform integrates with an internally developed payment system through REST APIs rather than connecting directly to a third-party payment gateway.
The payment flow follows a simple process:
- A tenant initiates a rental payment.
- The internal payment system processes the transaction.
- A webhook event is sent back to the platform.
- The corresponding payment and rental records are updated automatically.
- The property owner can see the updated payment status from the dashboard.
This integration reduces manual reconciliation and keeps rental and payment information synchronized across the two systems.
Since webhook events can occasionally be delivered more than once, transaction identifiers are checked before updating records. This prevents the same payment from being processed repeatedly.
The payment system handles the transaction process, while the boarding house platform remains responsible for the related rental and operational data.
Authentication & Authorization
Authentication and authorization are implemented using NextAuth, providing session management and access control throughout the application.
The platform supports multiple user roles, including property owners and tenants. Protected routes and authorization checks ensure users can only access features and data relevant to their role.
Application inputs are validated using Zod on both the client and server. For example, booking requests validate the room identifier, check-in date, rental duration, and optional notes before the request is processed.
const bookingSchema = z.object({ roomId: z.string().cuid(), checkIn: z.date().min(new Date()), duration: z.number().int().min(1).max(12), notes: z.string().max(500).optional(),});Using shared validation schemas helps keep frontend and backend validation consistent while reducing duplicated validation rules.
Media Management with Cloudinary
Property and room photos are managed through Cloudinary, allowing images to be uploaded, stored, transformed, and delivered efficiently.
Uploads are signed on the server so sensitive credentials are not exposed to the client. Cloudinary transformations are also used to generate optimized images and thumbnails, reducing unnecessary image processing within the application.
This keeps media storage separate from the application itself and allows the same assets to be served efficiently across property listings, room details, and other image-heavy views.
Project Status
The platform was completed between November 2025 and March 2026.
The implemented system covers the core property, room, tenant, booking, rental payment, authentication, and media workflows required by the business. The architecture also leaves room for extending operational features without restructuring the core domain model.
Key Takeaways
This project provided practical experience in building software around real operational workflows rather than isolated features.
The main challenge was keeping property, room, tenant, booking, rental, and payment information connected across both public and internal workflows. This influenced decisions around data modeling, validation, authorization, and integration with the internal payment system.
The project also reinforced the importance of keeping the architecture simple and maintainable as business requirements evolve. By keeping related workflows within a single application and establishing clear boundaries between different responsibilities, new operational features can be introduced without significantly restructuring the system.
