Back to Blog
Backend Development

Kemotives Server: The API Powering Kenya's Vehicle Marketplace

View Repository on GitHub

Overview

The Kemotives Server is the backbone of the Kemotives vehicle marketplace. It serves both the public marketplace (kemotives-market-place) and the admin dashboard (kemotives-admin), providing a single source of truth for all vehicle listing data.

Tech Stack

  • Node.js + Express — REST API
  • PostgreSQL + Drizzle ORM — Relational data for structured vehicle data
  • Redis — Caching for search results and popular listings
  • Cloudinary — Vehicle image hosting
  • M-Pesa Daraja API — Payment processing
  • Resend — Transactional email

Database Design

Vehicles have structured, queryable attributes — which is why I chose PostgreSQL over MongoDB. The Prisma schema enforces data integrity at the database level:

model Listing {
  id          String   @id @default(cuid())
  make        String
  model       String
  year        Int
  price       Int       // in KES
  mileage     Int       // in km
  condition   Condition // NEW | USED | CERTIFIED_PRE_OWNED
  bodyType    BodyType  // SEDAN | SUV | PICKUP | VAN
  transmission Transmission // MANUAL | AUTOMATIC
  fuelType    FuelType  // PETROL | DIESEL | ELECTRIC | HYBRID
  images      String[]  // Cloudinary public IDs
  dealerId    String
  dealer      Dealer    @relation(fields: [dealerId], references: [id])
  createdAt   DateTime  @default(now())
}

Search Architecture

Marketplace search is a performance-critical endpoint. I implemented a multi-layer approach:

  1. Redis cache — Cache the top 100 most-searched filter combinations for 5 minutes
  2. PostgreSQL full-text search — For make/model text queries using tsvector
  3. Indexed columnsyear, price, mileage, bodyType are all indexed for fast range queries

M-Pesa Integration

Handling the M-Pesa STK Push flow requires careful state management:

1. Client requests payment → Server initiates STK push
2. M-Pesa sends push notification to buyer's phone
3. Buyer enters PIN
4. M-Pesa POSTs result to our callback URL
5. Server validates callback signature and updates payment record
6. If no callback in 90s → poll Daraja query API as fallback

The callback validation step is critical — without it, anyone could spoof a successful payment.

Rate Limiting & Security

  • Helmet.js — Secure HTTP headers
  • express-rate-limit — 100 requests/minute for public endpoints, 20 for auth endpoints
  • bcrypt — Password hashing with cost factor 12
  • CORS — Whitelist of allowed origins (marketplace domain only)

View on GitHub →