Kemotives Server: The API Powering Kenya's Vehicle Marketplace
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:
- Redis cache — Cache the top 100 most-searched filter combinations for 5 minutes
- PostgreSQL full-text search — For make/model text queries using
tsvector - Indexed columns —
year,price,mileage,bodyTypeare 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)