A lightweight, multi-tenant CRM tuned for landlords and property managers. The project ships with a Node.js backend (no external npm dependencies) and a vanilla JavaScript UI, so everything can be executed on a macOS/iOS development machine without extra tooling.
- Multi-user (tenant) data separation using an
X-Tenant-Idheader. - User accounts (tenants), team members, properties, customers/residents, leases, and tasks resources with CRUD endpoints.
- JSON file storage (one file per deployment) keeps the footprint tiny while remaining persistent.
- Dashboard endpoint aggregates rent pipeline value, portfolio totals, and upcoming tasks.
- CORS enabled out of the box for the bundled frontend.
- End-to-end tests written with the built-in Node test runner (
node --test).
- Zero-build vanilla JS single-page app served from static files.
- Dashboard, Customers, Properties, Leases, Tasks, and Settings views.
- User picker and onboarding modal.
- Inline editing for resident stage, approval status, and lease stage/status.
- Task tracker with completion toggles.
- Settings view to change the API base URL and manage portfolio users.
- Users map to the multi-tenant
/tenantsendpoints and represent a landlord or property management account. - Properties reuse the
/companiesendpoints for buildings or individual rentals, with address, city, state, and unit counts captured directly on the record. - Customers/Residents reuse the
/contactsendpoints for tenant leads and occupants, and now include an approval status ofapproved,unapproved, orpendingalongside the pipeline stage. - Leases reuse the
/dealsendpoints and support stagesinquiry,tour scheduled,application submitted,screening,lease signedwith statusesactive,leased, orlost. - Tasks reuse the
/activitiesendpoints for maintenance, follow-ups, and showings.
CRM_v3/
├── server/ # Node.js backend (file-based persistence)
├── client/ # Static frontend (HTML/CSS/JS)
└── README.md # This file with setup details
- Node.js 20+ (for native
fetchand the built-in test runner). - A modern browser (Safari/Chrome/Firefox) to open the frontend. No bundler is required.
cd server
npm install # no external deps, but initialises package-lock for reproducibility
npm run dev # starts the API on http://localhost:4000The backend persists data to
server/data/crm-data.json. Remove the file to reset the environment.
The UI is plain static content, so you can serve it with any local HTTP server. A simple option using Python:
cd client
python3 -m http.server 5173Now open http://localhost:5173 in your browser. The UI points to http://localhost:4000/api by default; adjust the API base in Settings → API configuration if you change the backend URL.
- Launch the backend (
npm run devinserver). - Serve the frontend (e.g.
python3 -m http.serverinsideclient). - Visit the UI and create a user using the “New user” button.
- Select the user from the dropdown. The rest of the sections will now load.
- Use Settings to add team members, then manage customers (residents), properties, leases, and tasks.
All API endpoints live under /api. Most routes require an X-Tenant-Id header (except tenant creation/listing and health).
| Method | Path | Description |
|---|---|---|
| GET | /api/health |
Simple health probe |
| POST | /api/tenants |
Create a user record (tenant) |
| GET | /api/tenants |
List users (tenants) |
| GET | /api/users |
List team members for the selected user |
| POST | /api/users |
Create a user |
| GET | /api/contacts |
List customers/residents (supports search, status, approvalStatus) |
| POST | /api/contacts |
Create a customer/resident lead |
| PUT | /api/contacts/:id |
Update a customer/resident |
| DELETE | /api/contacts/:id |
Remove a customer/resident |
| GET | /api/companies |
List properties |
| POST | /api/companies |
Create a property |
| PUT | /api/companies/:id |
Update a property |
| GET | /api/deals |
List leases (filter by stage, status) |
| POST | /api/deals |
Create a lease opportunity |
| PUT | /api/deals/:id |
Update a lease |
| DELETE | /api/deals/:id |
Remove a lease |
| GET | /api/activities |
List tasks |
| POST | /api/activities |
Create a task |
| PUT | /api/activities/:id |
Update a task |
| DELETE | /api/activities/:id |
Remove a task |
| GET | /api/dashboard |
Aggregated metrics for the portfolio |
curl -X POST http://localhost:4000/api/contacts \
-H "Content-Type: application/json" \
-H "X-Tenant-Id: <tenant-id>" \
-d '{
"firstName": "Jordan",
"lastName": "Lee",
"email": "[email protected]",
"status": "prospect",
"approvalStatus": "pending"
}'Run the automated backend test suite from the server folder:
cd server
npm testThe tests spin up an isolated API server, create a full portfolio workflow, and verify that the dashboard aggregates data correctly.
- Because the backend has zero npm dependencies, deployment is as simple as copying the
serverdirectory to any machine with Node.js 20 and runningnpm run start. - The frontend can be hosted from any static file service (e.g. GitHub Pages, S3, or an nginx container). Update the API base URL in Settings after deployment.
- For production, consider running the backend behind a process manager like
pm2or a systemd service, and place theserver/datadirectory on persistent storage.
- Replace the JSON storage with a different persistence layer by modifying
server/src/storage/dataStore.js. - Add new resources by expanding the
DataStoreclass and wiring routes inserver/src/routes/index.js. - The frontend is modular; add new pages under
client/src/pagesand register them insrc/main.js.
Enjoy building on top of this landlord CRM foundation!