About
poc-vite-crud is a production-quality proof-of-concept web application for managing Comps (comparable real estate sales) records. It demonstrates a clean full-stack setup with a React SPA frontend, a RESTful Express API, and a PostgreSQL database — built with simplicity, correctness, and developer experience in mind.
Features
- Sortable, paginated table of Comps records (5 per page)
- Advanced filtering: Address, City, County, State, Zip Code, Sale Date range, Sale Price range
- Add / Edit / Delete records via a clean modal form
- Export the currently filtered and sorted dataset as a CSV file
- Server-side pagination with total record count
- Rate-limited REST API (
express-rate-limit) - Environment-based configuration via
.env/.env.example - Vite dev-server proxy — no CORS setup needed during development
Tech Stack
Frontend
React 18, Vite 5, plain CSS
Backend
Node.js 18+, Express 4, express-rate-limit, pg (node-postgres)
Database
PostgreSQL 14+
Tooling
npm, nodemon, dotenv, Vite proxy
Usage
Database
- Create DB:
CREATE DATABASE poc_vite_crud; - Run schema:
psql -U postgres -d poc_vite_crud -f db/schema.sql
Backend
- Install:
cd backend && npm install - Configure:
cp .env.example .envand fill in DB credentials - Dev:
npm run dev— API athttp://localhost:3001 - Production:
npm start
Frontend
- Install:
cd frontend && npm install - Configure:
cp .env.example .env(setVITE_BACKEND_URLandVITE_DEV_PORT) - Dev:
npm run dev— UI athttp://localhost:5173 - Build:
npm run build(output:dist/)
Environment Variables
- Backend (
backend/.env):DB_HOST,DB_PORT,DB_NAME,DB_USER,DB_PASSWORD,PORT - Frontend (
frontend/.env):VITE_BACKEND_URL(proxy target),VITE_DEV_PORT
API Reference
Base URL: http://localhost:3001
- List comps:
GET /comps— supports filters, sort, and pagination - Create comp:
POST /comps - Update comp:
PUT /comps/:id - Delete comp:
DELETE /comps/:id - Health check:
GET /health
GET /comps query parameters:
address,city,county,state,zipCode— partial ILIKE matchsaleDateFrom/saleDateTo— date range (YYYY-MM-DD)salePriceMin/salePriceMax— price rangesortBy— column name;sortDir—asc|descpage— page number (default: 1);limit— items per page (default: 5, pass 0 for all)
Response: { "data": [...], "total": N }
Contact
Maintainer: Manuel Rodríguez Camacho