A robust Node.js/Express REST API for managing professional and personal connections, companies, and employment positions. Features JWT authentication, MongoDB integration, and comprehensive logging.
This is an experimental project - the result of an experiment to understand whether "vibe coding" can be used to generate end-to-end web applications.
Important Notes:
- The bulk of this code is AI-generated
- This code is NOT optimized for production use
- This project serves as a proof-of-concept and learning exercise
- Use at your own risk and review thoroughly before any production deployment
- Runtime: Node.js
- Framework: Express.js (v5.1.0)
- Database: MongoDB with Mongoose ODM (v8.15.0)
- Authentication: JWT (JSON Web Tokens)
- Security: bcryptjs for password hashing
- CORS: Cross-origin resource sharing enabled
- Environment: dotenv for configuration management
- Node.js (v16 or higher)
- MongoDB Atlas account or local MongoDB installation
- npm or yarn package manager
-
Clone and navigate to backend directory:
cd backend
-
Install dependencies:
npm install
-
Environment Configuration: Create a
.env
file in the backend root:# Database Configuration MONGODB_URI=mongodb+srv://username:password@cluster.mongodb.net/connections # JWT Secret (use a strong, random string in production) JWT_SECRET=your_super_secure_jwt_secret_key_here # Server Configuration PORT=4000 NODE_ENV=development
-
Start the development server:
npm run dev
Or for production:
npm start
- Development:
http://localhost:4000/api
- Production:
https://your-backend-domain.com/api
- GET
/health
- Check server status
- GET
/api/users/exists
- Response:
{ "hasUsers": boolean }
- POST
/api/initialize
- Body:
{ "email": "admin@example.com", "password": "password" }
- Note: Only works when no users exist in the system
- POST
/api/login
- Body:
{ "email": "user@example.com", "password": "password" }
- Response:
{ "token": "jwt_token", "user": {...} }
- POST
/api/register
- Headers:
Authorization: Bearer <admin_jwt_token>
- Body:
{ "email": "newuser@example.com", "password": "password" }
- GET
/api/connections
- Headers:
Authorization: Bearer <jwt_token>
- GET
/api/connections/:id
- Headers:
Authorization: Bearer <jwt_token>
- Response: Includes connection details and associated positions
- POST
/api/connections
- Headers:
Authorization: Bearer <jwt_token>
- Body:
{ "name": "John Doe", "email": "john@example.com", "phone": "+1234567890", "linkedinUserId": "johndoe", "githubUserId": "johndoe", "notes": "Met at tech conference" }
- PUT
/api/connections/:id
- Headers:
Authorization: Bearer <jwt_token>
- Body: Same as create, with updated fields
- DELETE
/api/connections/:id
- Headers:
Authorization: Bearer <jwt_token>
- Note: Also deletes all associated positions
- GET
/api/connections/bycompany/:companyId
- Headers:
Authorization: Bearer <jwt_token>
- GET
/api/connections/search/:query
- Headers:
Authorization: Bearer <jwt_token>
- Description: Search connections across name, email, phone, LinkedIn/GitHub usernames
- Example:
/api/connections/search/john
- finds all connections matching "john" - Note: Search is case-insensitive and supports partial matches
- GET
/api/companies
- Headers:
Authorization: Bearer <jwt_token>
- GET
/api/companies/:id
- Headers:
Authorization: Bearer <jwt_token>
- Response: Includes company details and associated positions
- POST
/api/companies
- Headers:
Authorization: Bearer <jwt_token>
- Body:
{ "name": "Tech Corp Inc", "industry": "Technology", "website": "https://techcorp.com" }
- PUT
/api/companies/:id
- Headers:
Authorization: Bearer <jwt_token>
- Body: Same as create, with updated fields
- DELETE
/api/companies/:id
- Headers:
Authorization: Bearer <jwt_token>
- Note: Also deletes all associated positions
- GET
/api/companies/byconnection/:connectionId
- Headers:
Authorization: Bearer <jwt_token>
- GET
/api/companies/search/:query
- Headers:
Authorization: Bearer <jwt_token>
- Description: Search companies across name, industry, and website fields
- Example:
/api/companies/search/tech
- finds all companies matching "tech" - Note: Search is case-insensitive and supports partial matches
- GET
/api/positions
- Headers:
Authorization: Bearer <jwt_token>
- GET
/api/positions/:id
- Headers:
Authorization: Bearer <jwt_token>
- POST
/api/positions
- Headers:
Authorization: Bearer <jwt_token>
- Body:
{ "connectionId": "connection_object_id", "companyId": "company_object_id", "title": "Software Engineer", "startDate": "2023-01-15", "endDate": "2024-01-15", "current": false, "notes": "Full-stack development role" }
- PUT
/api/positions/:id
- Headers:
Authorization: Bearer <jwt_token>
- Body: Same as create, with updated fields
- DELETE
/api/positions/:id
- Headers:
Authorization: Bearer <jwt_token>
- GET
/api/positions/connection/:connectionId
- Headers:
Authorization: Bearer <jwt_token>
- GET
/api/positions/company/:companyId
- Headers:
Authorization: Bearer <jwt_token>
{
_id: ObjectId,
email: String (required, unique),
password: String (required, hashed),
isAdmin: Boolean (default: false),
createdAt: Date,
updatedAt: Date
}
{
_id: ObjectId,
userId: String (required),
name: String (required),
email: String,
phone: String,
linkedinUserId: String,
githubUserId: String,
notes: String,
createdAt: Date,
updatedAt: Date
}
{
_id: ObjectId,
userId: String (required),
name: String (required),
industry: String,
website: String,
createdAt: Date,
updatedAt: Date
}
{
_id: ObjectId,
userId: String (required),
connectionId: ObjectId (ref: 'Connection', required),
companyId: ObjectId (ref: 'Company', required),
title: String (required),
startDate: Date,
endDate: Date,
current: Boolean (default: false),
notes: String,
createdAt: Date,
updatedAt: Date
}
- JWT Authentication: Secure token-based authentication
- Password Hashing: bcryptjs with salt rounds
- User Isolation: All data is user-scoped via userId
- Admin Controls: User registration restricted to admin users
- Request Logging: Comprehensive logging with sensitive data protection
- CORS Configuration: Cross-origin request handling
{
"start": "node src/index.js",
"dev": "nodemon src/index.js",
"test": "echo \"Error: no test specified\" && exit 1"
}
-
Create Render Web Service:
- Connect your GitHub repository
- Set build command:
npm install
- Set start command:
npm start
- Set environment to
Node
-
Environment Variables:
MONGODB_URI=mongodb+srv://... JWT_SECRET=your_production_jwt_secret NODE_ENV=production PORT=4000
-
MongoDB Atlas Setup:
- Create a MongoDB Atlas cluster
- Add your Render service IP to IP whitelist (or use 0.0.0.0/0 for all IPs)
- Create a database user with read/write permissions
- Heroku: Compatible with Heroku's Node.js buildpack
- Railway: Direct GitHub integration
- Vercel: Serverless functions (requires restructuring)
- DigitalOcean App Platform: Container-based deployment
- Create route file in
src/routes/
- Import and use in
src/index.js
- Follow existing authentication middleware pattern
- No formal migration system
- Use MongoDB Compass or Atlas UI for data management
- Consider implementing seeds for development data
The application includes comprehensive request logging:
- All requests are logged with timestamp and IP
- Sensitive data (passwords, emails, etc.) is hidden in logs
- Error logging includes stack traces for debugging
-
MongoDB Connection Error:
- Verify MONGODB_URI in .env
- Check Atlas IP whitelist
- Ensure database user has correct permissions
-
JWT Authentication Fails:
- Verify JWT_SECRET is set
- Check token format in Authorization header
- Ensure token hasn't expired
-
CORS Issues:
- Configure CORS_ORIGIN for production
- Verify frontend URL matches CORS settings
-
Port Already in Use:
- Change PORT in .env file
- Kill existing process:
lsof -ti:4000 | xargs kill -9
MIT License - see LICENSE file for details
Harikesh Kushwaha
For frontend documentation, see Frontend README.