Hireable LogoHireable
Backend API

Backend Overview

Express API architecture, structure, and patterns

Overview

The backend is built with Express and TypeScript, providing a REST API for the Hireable platform. It runs on port 3001 and connects to MongoDB.

Technology Stack

TechnologyVersionPurpose
Express4.18.2Web framework
TypeScript5.3.3Type safety
Mongoose9.0.0MongoDB ODM
CORS2.8.5Cross-origin requests
dotenv16.3.1Environment variables

Project Structure

apps/api/
├── src/
│   ├── app.ts                # Application entry point
│   ├── controllers/          # Request handlers
│   │   └── waitlist.controller.ts
│   └── routes/               # Route definitions
│       └── waitlist.routes.ts
├── dist/                     # Compiled output
├── .env                      # Environment variables
├── vercel.json               # Deployment config
├── tsconfig.json
└── package.json

Application Entry Point

// src/app.ts
import cors from "cors";
import dotenv from "dotenv";
import express from "express";
import { dbConnect } from "@hireable/database";
import waitlistRoutes from "./routes/waitlist.routes";
 
dotenv.config();
 
const app = express();
const PORT = process.env.PORT || 3001;
 
// Middleware
app.use(cors());
app.use(express.json());
 
// Database connection middleware
app.use(async (req, res, next) => {
  try {
    await dbConnect();
    next();
  } catch (error) {
    console.error("Database connection failed", error);
    res.status(500).json({ error: "Database connection failed" });
  }
});
 
// Routes
app.get("/", (req, res) => {
  res.send("API is running");
});
 
app.use("/api/waitlist", waitlistRoutes);
 
// Start server
const start = async () => {
  try {
    await dbConnect();
    app.listen(PORT, () => {
      console.info(`Server running on port ${PORT}`);
    });
  } catch (error) {
    console.error(error);
    process.exit(1);
  }
};
 
start();

Route Pattern

Routes define API endpoints and connect them to controllers:

// src/routes/waitlist.routes.ts
import { Router } from "express";
import { WaitlistController } from "../controllers/waitlist.controller";
 
const router = Router();
 
router.post("/employer", WaitlistController.joinEmployer);
router.post("/talent", WaitlistController.joinTalent);
 
export default router;

Controller Pattern

Controllers handle request/response logic:

// src/controllers/waitlist.controller.ts
import { Request, Response } from "express";
import { Employer, Talent } from "@hireable/database";
import { SecurityValidator } from "@hireable/shared";
 
export const WaitlistController = {
  async joinEmployer(req: Request, res: Response) {
    try {
      const { firstName, lastName, email, company, companySize, industry, role, agree } = req.body;
 
      // Validate input
      const emailValidation = SecurityValidator.validateEmail(email);
      if (!emailValidation.isValid) {
        return res.status(400).json({ message: emailValidation.errors[0] });
      }
 
      // Check for existing entry
      const existingUser = await Employer.findOne({
        email: emailValidation.sanitizedData,
      });
 
      if (existingUser) {
        return res.status(409).json({ 
          message: "This email is already on the waitlist." 
        });
      }
 
      // Create new entry
      const newEmployer = new Employer({
        firstName: firstNameValidation.sanitizedData,
        lastName: lastNameValidation.sanitizedData,
        email: emailValidation.sanitizedData,
        company: companyValidation.sanitizedData,
        companySize: companySizeValidation.sanitizedData,
        industry: industryValidation.sanitizedData,
        role: roleValidation.sanitizedData,
        agree: agree,
      });
 
      await newEmployer.save();
 
      return res.status(201).json({
        message: "Successfully added to waitlist",
        success: true,
        data: {
          id: newEmployer._id,
          timestamp: newEmployer.createdAt,
        },
      });
    } catch (error) {
      console.error("Error joining employer waitlist:", error);
      return res.status(500).json({ message: "Error saving data" });
    }
  },
 
  async joinTalent(req: Request, res: Response) {
    // Similar implementation for talent
  },
};

Shared Packages

The API uses shared packages for consistency:

@hireable/database

import { dbConnect, Employer, Talent } from "@hireable/database";
 
// Connect to database
await dbConnect();
 
// Use models
const employer = new Employer({ ... });
await employer.save();
 
const talent = await Talent.findOne({ email });

@hireable/shared

import { SecurityValidator } from "@hireable/shared";
 
// Validate input
const result = SecurityValidator.validateEmail(email);
if (!result.isValid) {
  return res.status(400).json({ message: result.errors[0] });
}
 
// Use sanitized data
const sanitizedEmail = result.sanitizedData;

Available Scripts

# Development with hot reload
npm run dev
 
# Build for production
npm run build
 
# Start production server
npm run start
 
# Run linting
npm run lint

Environment Variables

# Server port
PORT=3001
 
# MongoDB connection
MONGODB_URI=mongodb://localhost:27017/hireable
 
# CORS origins (production)
CORS_ALLOWED_ORIGINS=https://hireable.ph

API Base URL

  • Development: http://localhost:3001/api
  • Production: Configured via NEXT_PUBLIC_API_URL

Next Steps

On this page