FutureRent API is a comprehensive property management system built with Node.js, TypeScript, and Express. It provides endpoints for user management, property listings, rental agreements, and more.
All protected endpoints require a valid JWT token in the Authorization header.
Authorization: Bearer <your_jwt_token>
Authenticate user and return JWT token.
{
"email": "user@example.com",
"password": "password123"
}
Register a new user account.
{
"name": "John Doe",
"email": "john@example.com",
"password": "securepassword",
"role": "tenant"
}
Get current user profile (requires authentication).
Get list of properties with pagination and filtering.
GET /api/v1/properties?page=1&limit=10&type=apartment
Create a new property listing (requires landlord role).
{
"title": "Modern Apartment",
"description": "Spacious 2-bedroom apartment...",
"price": 1500,
"type": "apartment",
"location": {
"address": "123 Main St",
"city": "New York",
"state": "NY"
}
}
Validates JWT tokens and attaches user data to requests.
import { Request, Response, NextFunction } from 'express';
import jwt from 'jsonwebtoken';
export const authMiddleware = (req: Request, res: Response, next: NextFunction) => {
const token = req.header('Authorization')?.replace('Bearer ', '');
if (!token) {
return res.status(401).json({ error: 'Access denied. No token provided.' });
}
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET!);
(req as any).user = decoded;
next();
} catch (error) {
res.status(400).json({ error: 'Invalid token.' });
}
};
Logs all incoming requests and responses.
import { Request, Response, NextFunction } from 'express';
import ActivityLogger from '../utils/activityLogger';
export const activityLogMiddleware = (req: Request, res: Response, next: NextFunction) => {
ActivityLogger.logRequest(req, res, next);
};
export const errorLogMiddleware = (error: any, req: Request, res: Response, next: NextFunction) => {
ActivityLogger.logError(error, req, res);
next(error);
};
Comprehensive logging utility for request/response tracking.
static logRequest(req: Request, res: Response, next: NextFunction): void {
const startTime = Date.now();
const logData = this.createLogData(req, res, startTime);
// Check for authorization header
const authHeader = req.headers['authorization'] || req.headers['Authorization'];
if (!authHeader) {
res.status(401).json({
error: 'Access denied',
message: 'Authorization header is required',
});
ActivityLogger.logError({
message: 'Access denied - No authorization header provided',
stack: "Auth Controller: Authorization header is required",
name: "Auth Controller",
}, req, res)
return;
}
// Continue with request processing...
}
Standardized response format utility.
export class ResponseHelper {
static success(res: Response, data: any, message: string = 'Success', statusCode: number = 200) {
return res.status(statusCode).json({
success: true,
message,
data
});
}
static error(res: Response, message: string, statusCode: number = 500, errorDetails?: any) {
return res.status(statusCode).json({
success: false,
message,
error: errorDetails
});
}
}
Normal request/response logging for successful operations.
Error logging for failed requests and exceptions.
Authorization and application control events.
{
"logId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"timestamp": "2023-10-05T12:00:00.000Z",
"level": "info",
"message": "GET /api/v1/users/profile",
"requestId": "req-123456",
"userId": "user-789",
"ipAddress": "192.168.1.100",
"userAgent": "Mozilla/5.0...",
"method": "GET",
"url": "/api/v1/users/profile",
"headers": { "authorization": "***REDACTED***" },
"responseStatus": 200,
"responseTime": 45
}
export class AppError extends Error {
public statusCode: number;
public isOperational: boolean;
constructor(message: string, statusCode: number = 500) {
super(message);
this.statusCode = statusCode;
this.isOperational = true;
Error.captureStackTrace(this, this.constructor);
}
}
export class ValidationError extends AppError {
constructor(message: string, details?: any) {
super(message, 400);
this.name = 'ValidationError';
}
}
export class AuthenticationError extends AppError {
constructor(message: string = 'Authentication failed') {
super(message, 401);
this.name = 'AuthenticationError';
}
}
export const errorHandler = (err: any, req: Request, res: Response, next: NextFunction) => {
// Log error
ActivityLogger.logError(err, req, res);
// Default error
let error = { ...err };
error.message = err.message;
// Mongoose bad ObjectId
if (err.name === 'CastError') {
const message = 'Resource not found';
error = new AppError(message, 404);
}
// Mongoose duplicate key
if (err.code === 11000) {
const message = 'Duplicate field value entered';
error = new AppError(message, 400);
}
// Mongoose validation error
if (err.name === 'ValidationError') {
const message = Object.values(err.errors).map((val: any) => val.message);
error = new AppError(message.join(', '), 400);
}
res.status(error.statusCode || 500).json({
success: false,
message: error.message || 'Server Error'
});
};
import { Request, Response } from 'express';
import { User } from '../models/User';
import { ResponseHelper } from '../utils/responseHelper';
import { AppError } from '../errors/AppError';
import ActivityLogger from '../utils/activityLogger';
export class UserController {
static async getProfile(req: Request, res: Response) {
try {
const userId = (req as any).user.id;
const user = await User.findById(userId).select('-password');
if (!user) {
throw new AppError('User not found', 404);
}
ActivityLogger.logAuthorizationEvent('GRANTED', 'User profile accessed', req, {
userId: user.id,
action: 'profile_view'
});
return ResponseHelper.success(res, user, 'Profile retrieved successfully');
} catch (error) {
ActivityLogger.logError(error, req, res);
return ResponseHelper.error(res, 'Failed to retrieve profile', 500, error);
}
}
static async updateProfile(req: Request, res: Response) {
try {
const userId = (req as any).user.id;
const { name, email } = req.body;
const user = await User.findByIdAndUpdate(
userId,
{ name, email },
{ new: true, runValidators: true }
).select('-password');
if (!user) {
throw new AppError('User not found', 404);
}
ActivityLogger.logAppControlEvent('PROFILE_UPDATE', 'User profile updated', {
userId: user.id,
updatedFields: Object.keys(req.body)
}, req);
return ResponseHelper.success(res, user, 'Profile updated successfully');
} catch (error) {
ActivityLogger.logError(error, req, res);
return ResponseHelper.error(res, 'Failed to update profile', 500, error);
}
}
}
import express from 'express';
import { UserController } from '../controllers/UserController';
import { authMiddleware } from '../middleware/authMiddleware';
import { activityLogMiddleware } from '../middleware/activityLogMiddleware';
const router = express.Router();
// Apply activity logging to all routes
router.use(activityLogMiddleware);
// Public routes
router.post('/auth/register', UserController.register);
router.post('/auth/login', UserController.login);
// Protected routes (require authentication)
router.use(authMiddleware);
router.get('/users/profile', UserController.getProfile);
router.put('/users/profile', UserController.updateProfile);
export default router;