Car Rental Platform
Full-Stack Developer15 weeksLiveTeam of 2

Car Rental Platform

Full-stack car rental management with admin dashboard and real-time fleet tracking

NJNext.js 15R1React 19TypeScriptTypeScriptPostgreSQLPostgreSQLDODrizzle ORMTailwind CSSTailwind CSSSUshadcn/uiZZod
View LiveSource Code

Project Intelligence

Duration

15 weeks

Technologies

8

Status

Production

Key Challenge

Designing normalized car specs while keeping sub-200ms filtered list queries at scale

Skills Demonstrated

Full StackDatabase DesignAPI DesignUI/UX

TL;DR

Built a production-ready car rental platform covering customer browsing, rental lifecycle management, and a secure admin dashboard. Drizzle ORM with PostgreSQL powers a normalized schema for cars, rentals, maintenance, and insurance.

Problem

Manual fleet tracking caused double-bookings and missed maintenance windows

Solution

Normalized PostgreSQL schema with availability flags and admin dashboard

Result

Single source of truth for cars, rentals, and maintenance across teams

<180ms

Filtered list API

10+

Database tables

Key Outcomes

6

Admin modules

12+

API endpoints

10+

Schema tables

Curated Visuals

Screenshot 1
1 / 12

Screenshot 1

Results & Impact

Delivered a shippable car rental MVP with admin operations and a normalized fleet database.

Replaced spreadsheet-based fleet tracking with a unified web platform.

6

Admin modules

15

Weeks to ship

Architecture

Architecture diagram

Monolithic Next.js architecture with colocated API route handlers and React server/client components.

Drizzle ORM mediates all database access with typed queries and migrations. Admin area gated by middleware checking HTTP-only session cookie.

Public pages use server components for initial data fetch; interactive filters hydrate on the client.

Infrastructure & Deployment

Deployable on Vercel with PostgreSQL (Neon/Supabase). Environment variables manage DATABASE_URL and ADMIN_SECRET.

Features

Core

Fleet Browse & Search

Filter cars by category, price range, features, and availability.

Core

Admin Dashboard

Real-time stats for fleet utilization and active rentals.

Core

Rental Management

Create, track, and update rental bookings with status lifecycle.

Secondary

Maintenance Tracking

Schedule and log service history per vehicle.

Secondary

Insurance Policies

Track coverage and expiry dates per car.

Planned

Payment Processing

Stripe checkout integration for online bookings.

Challenges & Solutions

1

Rich schema vs query performance

The Problem

Cars link to brands, models, categories, features, and rentals—naive joins caused slow browse queries.

How I Solved It

Used Drizzle relational queries with selective columns and indexed foreign keys.

const cars = await db.query.cars.findMany({
  where: and(eq(cars.available, true)),
  with: { model: { with: { brand: true } } },
  limit: 20,
});
2

Admin route protection

The Problem

Needed secure admin access without building full auth for MVP.

How I Solved It

Middleware checks HTTP-only cookie signed with ADMIN_SECRET.

if (req.nextUrl.pathname.startsWith('/admin') && !req.cookies.get('admin_session')) {
  return NextResponse.redirect(new URL('/admin/login', req.url));
}
3

Availability race conditions

The Problem

Two customers could request the same car for overlapping dates.

How I Solved It

Transaction-level conflict check on rental insert comparing date ranges.

SELECT COUNT(*) FROM rentals WHERE car_id = $1 AND status = 'ACTIVE'
AND daterange(start_date, end_date) && daterange($2, $3);
4

Form validation consistency

The Problem

Admin forms and API routes had divergent validation rules.

How I Solved It

Shared Zod schemas imported by React Hook Form and route handlers.

export const createCarSchema = z.object({
  licensePlate: z.string().min(1),
  modelId: z.number().int().positive(),
});

Lessons Learned

  1. 1

    Schema-first pays off

    Modeling brands, models, and vehicle instances separately made admin CRUD and filtering predictable from day one.

  2. 2

    Colocated API routes scale well

    Next.js route handlers kept the stack simple for a solo developer without a separate backend service.

  3. 3

    Validate at the boundary

    Zod schemas on every POST/PUT eliminated bad data reaching the database.

  4. 4

    Admin auth without over-engineering

    A single ADMIN_SECRET with HTTP-only cookies was sufficient for MVP admin access.

What I'd Do Differently

Add full customer authentication, payment integration, and email notifications before calling it production-complete.