Car Dealership Website
Listings, financing pages and an admin panel over a JWT-secured API
A dealership site lives or dies on two screens: the search a buyer uses, and the panel the sales team uses to keep stock current. This build treats them as one system — the same Express API feeds the public listings and the admin dashboard, with authentication sitting between them.
The brief
The dealership was listing cars in social media posts, which meant no search, no history and no way to tell which cars had already sold. They wanted a proper site — browse and filter stock, read financing options, submit a car for sale — plus an internal view that shows what is actually on the lot.
What I built
- Eight public pages plus a separate admin app — home, browse cars, car details, financing, sell your car, articles, about and contact.
- Vehicle listings with detail pages, driven by GET /api/vehicles and GET /api/vehicles/:id.
- Photo uploads for both vehicle listings and customer enquiries, handled with Multer.
- An admin dashboard with GET /api/admin/stats for a quick read of the business, plus the full vehicle management set.
- An enquiry pipeline — customers send a message, staff move it through statuses with PATCH /api/messages/:id/status.
- A save-a-car table (saved_vehicles) so a visitor can shortlist without emailing links to themselves.
How it works
The API is split by concern rather than dumped in one file: authRoutes, vehicleRoutes, adminRoutes and messageRoutes. Sign-in issues a JWT, and a thin auth.js middleware does two jobs — verify the token on every protected request, and check the role before letting anyone near the admin routes, returning a flat 403 otherwise.
Passwords are hashed with bcrypt before storage, and the app runs behind Helmet for sane security headers. Uploads go through Multer with a 5 MB cap and an image-only mime filter, so a mislabelled file cannot land in the uploads folder. The first administrator is created by a small seed-admin.js script rather than by hand-editing the database.
What I would do next
- Add pagination to the listings endpoint — fine at demo scale, but a real forecourt will eventually return thousands of rows in one response.
- Serve vehicle photos in a few widths instead of full-size, so a phone is not downloading desktop-sized images.
- Add an audit trail to admin edits so a price change can be traced to a person and a time.