A robust, scalable backend API for the RuralSync rural services marketplace platform.
- Overview
- Architecture
- Design Principles
- Project Structure
- Getting Started
- API Endpoints
- Authentication
- Environment Variables
- Database Models
- AI Features
RuralSync API is a modular monolith backend that powers a marketplace connecting rural service providers (farmers, equipment rentals, repair services) with customers. The API supports three distinct user roles:
- Clients - End users who search for and book services
- Service Providers - Businesses that offer services and manage agents
- Agents - Field workers assigned to fulfill bookings
- π Role-Based Authentication with JWT & HTTP-only Cookies
- π AI-Powered Search using Vector Embeddings (Hugging Face)
- π Geospatial Queries for location-based service discovery
- π Real-time Availability checking for bookings
- β Review & Rating System for service quality
- π Audit Logging for tracking system changes
- π Swagger Documentation for API exploration
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β API Gateway β
β (Express.js + CORS) β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β βββββββββββββββ βββββββββββββββ βββββββββββββββ βββββββββββββββ β
β β Auth β β Client β β Provider β β Agent β β
β β Module β β Module β β Module β β Module β β
β ββββββββ¬βββββββ ββββββββ¬βββββββ ββββββββ¬βββββββ ββββββββ¬βββββββ β
β β β β β β
β ββββββββΌβββββββββββββββββΌβββββββββββββββββΌβββββββββββββββββΌβββββββ β
β β Shared Services Layer β β
β β (Notification Strategies, AI Helpers, Validation, Utilities) β β
β ββββββββββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββββ β
β β β
β ββββββββββββββββββββββββββββββββΌββββββββββββββββββββββββββββββββββ β
β β Data Access Layer β β
β β (Mongoose Models & Repositories) β β
β ββββββββββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββββ β
β β β
ββββββββββββββββββββββββββββββββββββΌβββββββββββββββββββββββββββββββββββββββ
β
ββββββββββββββββΌβββββββββββββββ
β MongoDB β
β (Atlas with Vector Search) β
βββββββββββββββββββββββββββββββ
The codebase is organized into feature modules (auth, client, provider, agent) that are loosely coupled but deployed as a single unit. This provides:
- β Clear domain boundaries
- β Easier refactoring to microservices later
- β Simplified deployment and debugging
- β Shared infrastructure (DB, cache) without network overhead
modules/
βββ auth/ # Authentication & User Management
βββ client/ # Customer-facing features
βββ provider/ # Service Provider dashboard features
βββ agent/ # Agent mobile app features
βββ shared/ # Cross-cutting concerns
Controllers receive their dependencies (services) via constructor injection, making the code:
- β Testable - Easy to mock dependencies
- β Flexible - Swap implementations without changing consumers
- β Explicit - Dependencies are clearly visible
// modules/provider/index.ts - Composition Root
const profileService = new ProviderProfileService(Organization, ServiceProvider);
const profileController = new ProviderProfileController(profileService);
export { profileController };Notifications use the Strategy Pattern, allowing different delivery mechanisms (Email, SMS, Push) to be plugged in without modifying business logic:
// Interface
interface INotificationStrategy {
send(payload: INotificationPayload): Promise<boolean>;
}
// Concrete Strategy
class EmailNotificationStrategy implements INotificationStrategy {
async send({ to, subject, message }) {
// Send email logic
}
}
// Usage in Service
const bookingService = new ProviderBookingService({
notificationStrategies: { email: new EmailNotificationStrategy() }
});Services interact with Mongoose Models through a consistent interface, abstracting database operations:
class ProviderInventoryService {
constructor(
private serviceModel: Model<IService>,
private orgModel: Model<IOrganization>
) {}
async addService(userId: string, data: any) {
// Business logic + model operations
}
}Authentication and authorization are handled through composable middleware:
// Route definition with middleware chain
router.post('/add-service',
verifyJWT('SERVICE_PROVIDER'), // 1. Verify token & role
isAuthorized(['SERVICE_PROVIDER']), // 2. Check permissions
upload.fields([...]), // 3. Handle file uploads
inventoryController.create // 4. Execute business logic
);Each class has one reason to change:
| Layer | Responsibility |
|---|---|
| Controllers | HTTP request/response handling |
| Services | Business logic & orchestration |
| Models | Data structure & validation |
| Middleware | Cross-cutting concerns (auth, logging) |
| Utils | Reusable helper functions |
All configuration is externalized via environment variables with sensible defaults:
const getAllowedOrigins = (): string[] => {
const envOrigins = process.env.CORS_ORIGINS;
if (envOrigins) {
return envOrigins.split(',').map(origin => origin.trim());
}
return ["http://localhost:3000"]; // Default
};Consistent error responses using a custom error class:
class ApiError extends Error {
constructor(message: string, public status: number) {
super(message);
}
}
// Usage
throw new ApiError('Unauthorized: No token provided', 401);
// Global handler catches and formats
app.use((err, req, res, next) => {
res.status(err.status || 500).json({
success: false,
message: err.message,
stack: process.env.NODE_ENV === 'development' ? err.stack : undefined
});
});Services are indexed with AI-generated vector embeddings for semantic search:
// Auto-generate embeddings on save
serviceSchema.pre('save', async function(next) {
if (this.isModified('description')) {
const text = `${this.name} ${this.description} ${this.category}`;
this.embeddings = await generateEmbedding(text);
}
next();
});
// Vector search in aggregation pipeline
pipeline.push({
$vectorSearch: {
index: "vector_index",
path: "embeddings",
queryVector: await generateEmbedding(searchQuery),
numCandidates: 100,
limit: 15
}
});api/
βββ src/
β βββ app.ts # Express app configuration
β βββ server.ts # Server entry point
β β
β βββ config/
β β βββ db.ts # MongoDB connection
β β βββ redis.ts # Redis configuration (optional)
β β βββ swagger.ts # OpenAPI/Swagger setup
β β
β βββ middleware/
β β βββ auth.middleware.ts # JWT verification & RBAC
β β βββ error.middleware.ts# Global error handler
β β βββ upload.middleware.ts # Multer file upload
β β
β βββ models/
β β βββ agent.model.ts
β β βββ booking.model.ts
β β βββ client.model.ts
β β βββ organization.model.ts
β β βββ review.model.ts
β β βββ service.model.ts
β β βββ serviceProvider.model.ts
β β
β βββ modules/
β β βββ auth/
β β β βββ auth.routes.ts
β β β βββ auth.controller.ts
β β β βββ auth.service.ts
β β β βββ index.ts # Composition root
β β β
β β βββ client/
β β β βββ client.routes.ts
β β β βββ controllers/
β β β βββ services/
β β β βββ index.ts
β β β
β β βββ provider/
β β β βββ provider.routes.ts
β β β βββ controllers/
β β β βββ services/
β β β βββ index.ts
β β β
β β βββ agent/
β β β βββ ...
β β β
β β βββ shared/
β β βββ interfaces/
β β β βββ notification.interface.ts
β β βββ providers/
β β βββ email.provider.ts
β β
β βββ utils/
β βββ helpers.ts # ApiError, JWT utils, Multer
β βββ ai.helper.ts # Hugging Face embeddings
β βββ s3.ts # AWS S3 uploads (optional)
β
βββ .env.example
βββ package.json
βββ tsconfig.json
βββ README.md
- Node.js >= 18.x
- MongoDB >= 7.x (or MongoDB Atlas)
- npm or yarn
# Clone the repository
git clone https://github.com/Ayush-Vish/RS-Monolith.git
cd RS-Monolith/api
# Install dependencies
npm install
# Copy environment variables
cp .env.example .env
# Edit .env with your values
nano .env
# Start development server
npm run dev| Command | Description |
|---|---|
npm run dev |
Start development server with hot-reload |
npm run build |
Compile TypeScript to JavaScript |
npm start |
Run production build |
| Method | Endpoint | Description | Auth |
|---|---|---|---|
| POST | /auth/register |
Register new user | Public |
| POST | /auth/login |
Login user | Public |
| POST | /auth/agent-register |
Register agent (by provider) | Provider |
| GET | /auth/logout |
Logout user | Any |
| GET | /auth/user-detail/:role |
Get user profile | Role-specific |
| Method | Endpoint | Description | Auth |
|---|---|---|---|
| GET | /client/search |
Search services (geo + text) | Public |
| GET | /client/search/categories |
Get all categories | Public |
| GET | /client/search/advanced |
Advanced search with filters | Public |
| GET | /client/services |
List all services | Public |
| GET | /client/services/:id |
Get service details | Public |
| GET | /client/profile |
Get client profile | Client |
| PUT | /client/profile |
Update profile | Client |
| POST | /client/bookings |
Create booking | Client |
| GET | /client/bookings |
Get my bookings | Client |
| DELETE | /client/bookings/:id |
Cancel booking | Client |
| POST | /client/reviews |
Create review | Client |
| GET | /client/reviews/my |
Get my reviews | Client |
| Method | Endpoint | Description | Auth |
|---|---|---|---|
| GET | /provider/org-detail |
Get organization details | Provider |
| POST | /provider/register-org |
Register organization | Provider |
| GET | /provider/services |
Get my services | Provider |
| POST | /provider/add-service |
Add new service | Provider |
| DELETE | /provider/delete-service/:id |
Delete service | Provider |
| GET | /provider/agents |
Get all agents | Provider |
| POST | /provider/assign-agent |
Assign agent to service | Provider |
| GET | /provider/bookings |
Get all bookings | Provider |
| POST | /provider/assign-booking |
Assign agent to booking | Provider |
| Method | Endpoint | Description | Auth |
|---|---|---|---|
| GET | /agent/bookings |
Get assigned bookings | Agent |
| PATCH | /agent/bookings/:id/status |
Update booking status | Agent |
| GET | /agent/profile |
Get agent profile | Agent |
1. User logs in with email/password
2. Server validates credentials
3. Server generates JWT tokens:
- accessToken (15 mins expiry)
- refreshToken (7 days expiry)
4. Tokens are sent as HTTP-only cookies
5. Client includes cookies in subsequent requests
6. Server validates token on protected routes
| Role | Cookie Name |
|---|---|
| Client | accessTokenClient |
| Service Provider | accessTokenServiceProvider |
| Agent | accessTokenAgent |
// Single role
router.get('/profile', verifyJWT('CLIENT'), controller.getProfile);
// Multiple roles allowed
router.get('/dashboard',
verifyJWT('ANY'),
isAuthorized(['CLIENT', 'SERVICE_PROVIDER']),
controller.getDashboard
);# Server Configuration
NODE_ENV=development # development | production
PORT=5000 # Server port
# Database
MONGO_URI=mongodb://127.0.0.1:27017/ruralsync
# JWT Secret (use strong secret in production!)
JWT_SECRET=your-super-secret-jwt-key-change-in-production
# CORS Origins (comma-separated for production)
CORS_ORIGINS=http://localhost:3000,http://localhost:5173
# AI Features (Optional - Hugging Face)
HF_TOKEN=your-huggingface-token
# AWS S3 (Optional - File Uploads)
AWS_REGION=ap-south-1
AWS_ACCESS_KEY_ID=your-access-key
AWS_SECRET_ACCESS_KEY=your-secret-key
AWS_BUCKET_NAME=your-bucket-nameβββββββββββββββββββ βββββββββββββββββββ
β ServiceProviderββββββββΆβ Organization β
β (User) β 1:1 β β
ββββββββββ¬βββββββββ ββββββββββ¬βββββββββ
β β
β 1:N β 1:N
βΌ βΌ
βββββββββββββββββββ βββββββββββββββββββ
β Agent βββββββββ Service β
β β N:M β β
ββββββββββ¬βββββββββ ββββββββββ¬βββββββββ
β β
β 1:N β 1:N
βΌ βΌ
βββββββββββββββββββ βββββββββββββββββββ
β Booking βββββββββ Client β
β β N:1 β (User) β
ββββββββββ¬βββββββββ ββββββββββ¬βββββββββ
β β
β 1:1 β 1:N
βΌ βΌ
βββββββββββββββββββ βββββββββββββββββββ
β AuditLog β β Review β
β β β β
βββββββββββββββββββ βββββββββββββββββββ
| Model | Key Features |
|---|---|
| Service | Geo-indexed location, AI embeddings, availability schedule |
| Booking | Status workflow, agent assignment, extra tasks support |
| Organization | Business hours, social media, verification status |
| Review | Rating aggregation, linked to service & provider |
The API uses Hugging Face Inference API to generate embeddings for services, enabling semantic search:
// Generate embedding for text
import { HfInference } from "@huggingface/inference";
const hf = new HfInference(process.env.HF_TOKEN);
export const generateEmbedding = async (text: string): Promise<number[]> => {
const result = await hf.featureExtraction({
model: "sentence-transformers/all-MiniLM-L6-v2", // 384 dimensions
inputs: text,
});
return result as number[];
};Create a vector search index in Atlas:
{
"mappings": {
"dynamic": true,
"fields": {
"embeddings": {
"type": "knnVector",
"dimensions": 384,
"similarity": "cosine"
}
}
}
}// 1. Convert query to vector
const queryVector = await generateEmbedding("tractor repair near me");
// 2. Vector search stage
{ $vectorSearch: {
index: "vector_index",
path: "embeddings",
queryVector,
numCandidates: 100,
limit: 15
}}
// 3. Geo filter stage
{ $match: {
location: {
$geoWithin: {
$centerSphere: [[lng, lat], radiusKm / 6378.1]
}
}
}}
// 4. Category filter
{ $match: { category: "Farm Equipment" }}Interactive API documentation is available via Swagger UI:
http://localhost:5000/api-docs
# Run tests (when implemented)
npm test
# Run with coverage
npm run test:coverageThe API is configured for Vercel deployment with:
- Memory storage for file uploads (4MB limit)
- Environment variables via Vercel dashboard
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY dist ./dist
EXPOSE 5000
CMD ["node", "dist/server.js"]This project is licensed under the ISC License.
- Ayush Vishwakarma - GitHub