/how-to-build-replit

How to Build a Recruitment platform with Replit

Learn how to build a recruitment platform using Replit. Follow step-by-step guidance to create, deploy, and optimize your hiring web app easily.

Matt Graham, CEO of Rapid Developers

Book a call with an Expert

Starting a new venture? Need to upgrade your web app? RapidDev builds application with your growth in mind.

How to Build a Recruitment platform with Replit

To build a recruitment platform on Replit, you’ll use Node.js (Express) for the backend, React for the frontend, and Replit’s built-in Database (for a prototype) or external database (like MongoDB Atlas) for persistent data. You’ll organize your project in a full-stack structure: the server runs in index.js at the root, and your client code (React) will live in a client folder. You’ll manage environment secrets in the “Secrets” tab (lock icon), use Replit’s “Run” button to serve, and optionally link a GitHub repo for versioning. The simplest Replit stack to start is the Node.js template with a Create React App frontend embedded manually.

 

Step 1: Project Setup

 

Open Replit and start a new Repl using the Node.js template. Then, in the Shell (bottom pane), run this to install the packages you’ll need:

npm init -y
npm install express cors body-parser
npx create-react-app client

This creates your backend and a React frontend inside a folder named client.

 

Step 2: Backend – Express Server

 

In the main Repl root, open or create index.js. This file will be where you define the server, endpoints (like APIs for job listings or applicants), and connections to the database.

// index.js
const express = require('express')
const bodyParser = require('body-parser')
const cors = require('cors')
const app = express()
const PORT = process.env.PORT || 3000

app.use(cors())
app.use(bodyParser.json())

// In-memory data for demonstration; replace with real DB later
let jobs = [
  { id: 1, title: 'Frontend Developer', company: 'TechCo', description: 'React experience required' },
  { id: 2, title: 'Backend Developer', company: 'DataCorp', description: 'Node.js & MongoDB skills needed' }
]

// API route to get all jobs
app.get('/api/jobs', (req, res) => {
  res.json(jobs)
})

// API route to post a new job
app.post('/api/jobs', (req, res) => {
  const newJob = { id: jobs.length + 1, ...req.body }
  jobs.push(newJob)
  res.json(newJob)
})

// Start server
app.listen(PORT, () => console.log(`Server running on port ${PORT}`))

Place this code directly in your index.js file (overwriting any placeholder Replit boilerplate). When you click “Run,” you’ll see “Server running on port 3000” in the console.

 

Step 3: Connect Frontend (React)

 

Navigate into the client folder and start it once locally (from the Shell) using:

cd client
npm start

Then stop that process (Ctrl+C). Back in Replit, you can serve the built React files through your Express backend. Inside client, build the static files by running:

npm run build

This creates a client/build folder. You’ll tell Express to serve it so users see the React UI when visiting your Repl URL.

Add this near the bottom of your index.js before app.listen():

const path = require('path')

// Serve React static files
app.use(express.static(path.join(__dirname, 'client/build')))

// Serve frontend for any unknown routes
app.get('*', (req, res) => {
  res.sendFile(path.join(__dirname, 'client/build', 'index.html'))
})

Now when the Repl runs, React and Express are integrated into a single deployable app.

 

Step 4: Frontend Example – Listing Jobs

 

Open client/src/App.js and replace its contents with the following simple recruitment UI to display and post jobs.

// client/src/App.js
import React, { useState, useEffect } from 'react'

function App() {
  const [jobs, setJobs] = useState([])
  const [newJob, setNewJob] = useState({ title: '', company: '', description: '' })

  useEffect(() => {
    fetch('/api/jobs')
      .then(res => res.json())
      .then(data => setJobs(data))
  }, [])

  const handleSubmit = (e) => {
    e.preventDefault()
    fetch('/api/jobs', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(newJob)
    })
    .then(res => res.json())
    .then(data => setJobs([...jobs, data]))

    setNewJob({ title: '', company: '', description: '' })
  }

  return (
    <div style={{ padding: '2rem', fontFamily: 'sans-serif' }}>
      <h2>Recruitment Platform</h2>

      <ul>
        {jobs.map(job => (
          <li key={job.id}>
            <strong>{job.title}</strong> at {job.company} – {job.description}
          </li>
        ))}
      </ul>

      <form onSubmit={handleSubmit} style={{ marginTop: '2rem' }}>
        <input
          placeholder='Job title'
          value={newJob.title}
          onChange={(e) => setNewJob({ ...newJob, title: e.target.value })}
          required
        />
        <input
          placeholder='Company'
          value={newJob.company}
          onChange={(e) => setNewJob({ ...newJob, company: e.target.value })}
          required
        />
        <input
          placeholder='Description'
          value={newJob.description}
          onChange={(e) => setNewJob({ ...newJob, description: e.target.value })}
          required
        />
        <button>Add Job</button>
      </form>
    </div>
  )
}

export default App

This code pulls job data from your Express API and renders it, letting users post new jobs with a form. The fetch('/api/...') URLs automatically talk to your backend through Replit’s served domain.

 

Step 5: Handling Secrets and Database

 

If you plan to use an external database (for example, MongoDB Atlas), open the “Secrets” tab in Replit (the lock icon in the left sidebar). Add a key MONGODB\_URI with your connection string. Then, replace the in-memory jobs array with a real database connection in index.js.

// Example snippet for MongoDB integration
const mongoose = require('mongoose')
mongoose.connect(process.env.MONGODB_URI, { useNewUrlParser: true, useUnifiedTopology: true })

const Job = mongoose.model('Job', { title: String, company: String, description: String })

Once connected, modify API routes to use Job.find() or Job.create() instead of working with the local jobs array.

 

Step 6: Deploy and Collaborate

 

Replit automatically deploys your app when you press “Run.” Share the URL (it looks like https://your-repl-name.username.repl.co) to allow teammates or testers to access. Use “Invite” from the top-right corner to let collaborators edit in realtime.

If you want a persistent live deployment independent of your coding session, use the “Deployments” tab (next to the “Run” button) and choose a “Web Server” deployment. This keeps your app alive even when you close the tab.

 

Step 7: Common Replit Gotchas

 

  • Filesystem resets: Replit’s filesystem may reset inactive ephemeral Repls. Store data in an external DB or the built-in Replit DB if you want persistence.
  • Single port limitation: Your Express app must use the single exposed port (process.env.PORT). You can’t run React dev server separately in the same Repl long-term — build and serve it through Express.
  • Secrets: Never hardcode credentials in files; always add them under Secrets.
  • Performance: Replit’s server is great for prototypes and small deployments. For heavy traffic or file upload systems, move the backend later to a dedicated host.

 

With this structure, your recruitment platform runs fully within Replit — a working backend, frontend, and database-ready architecture that’s simple to maintain and deploy for demos or MVPs.

Want to explore opportunities to work with us?

Connect with our team to unlock the full potential of no-code solutions with a no-commitment consultation!

Contact Us

How to Build a Simple Job Posting and Application API with Express and Replit Database



import express from "express";
import { v4 as uuid } from "uuid";
import Database from "@replit/database";

const app = express();
const db = new Database();

app.use(express.json());

// Create new job post
app.post("/api/jobs", async (req, res) => {
  const id = uuid();
  const job = { id, ...req.body, createdAt: Date.now() };
  const jobs = (await db.get("jobs")) || [];
  await db.set("jobs", [...jobs, job]);
  res.status(201).json(job);
});

// Apply to a job and link candidate
app.post("/api/jobs/:id/apply", async (req, res) => {
  const { id } = req.params;
  const candidate = { id: uuid(), jobId: id, ...req.body, createdAt: Date.now() };
  const candidates = (await db.get("candidates")) || [];
  await db.set("candidates", [...candidates, candidate]);
  res.status(201).json(candidate);
});

// Fetch job with matching candidates for recruiter dashboard
app.get("/api/jobs/:id/details", async (req, res) => {
  const { id } = req.params;
  const jobs = (await db.get("jobs")) || [];
  const candidates = (await db.get("candidates")) || [];
  const job = jobs.find(j => j.id === id);
  if (!job) return res.status(404).json({ error: "Job not found" });
  const applicants = candidates.filter(c => c.jobId === id);
  res.json({ ...job, applicants });
});

const port = process.env.PORT || 3000;
app.listen(port, () => console.log(`Server running on port ${port}`));

How to Import Candidate Data from LinkedIn into Your Recruitment Platform



import express from "express";
import axios from "axios";
import Database from "@replit/database";

const app = express();
const db = new Database();
app.use(express.json());

// Import candidate data from external LinkedIn-like API using stored secret
app.post("/api/import/linkedin", async (req, res) => {
  const { profileUrl } = req.body;
  if (!profileUrl) return res.status(400).json({ error: "Profile URL required" });

  try {
    const resp = await axios.get("https://api.linkedin-example.com/v1/profile", {
      params: { url: profileUrl },
      headers: { Authorization: `Bearer ${process.env.LINKEDIN_API_KEY}` },
    });

    const candidate = {
      id: resp.data.id,
      name: resp.data.name,
      headline: resp.data.headline,
      skills: resp.data.skills,
      importedAt: Date.now(),
    };

    const stored = (await db.get("importedCandidates")) || [];
    stored.push(candidate);
    await db.set("importedCandidates", stored);

    res.json({ message: "Candidate imported successfully", candidate });
  } catch (err) {
    res.status(500).json({ error: "Import failed", details: err.message });
  }
});

const port = process.env.PORT || 3000;
app.listen(port, () => console.log(`Recruitment import service running on ${port}`));

How to Handle Candidate Status Updates via Webhooks in Your Recruitment Platform



import express from "express";
import Database from "@replit/database";

const app = express();
const db = new Database();
app.use(express.json());

// Handle external webhook to update candidate status (e.g. from automated assessment service)
app.post("/api/webhooks/assessment", async (req, res) => {
  const { candidateId, newStatus, score } = req.body;

  if (!candidateId || !newStatus)
    return res.status(400).json({ error: "candidateId and newStatus required" });

  const candidates = (await db.get("candidates")) || [];
  const idx = candidates.findIndex(c => c.id === candidateId);

  if (idx === -1)
    return res.status(404).json({ error: "Candidate not found" });

  candidates[idx] = {
    ...candidates[idx],
    status: newStatus,
    score: score ?? candidates[idx].score,
    updatedAt: Date.now()
  };

  await db.set("candidates", candidates);
  res.json({ message: "Candidate status updated", candidate: candidates[idx] });
});

const port = process.env.PORT || 3000;
app.listen(port, () => console.log(`Webhook listener running on port ${port}`));

Want to explore opportunities to work with us?

Connect with our team to unlock the full potential of no-code solutions with a no-commitment consultation!

Contact Us
Matt Graham, CEO of Rapid Developers

Book a call with an Expert

Starting a new venture? Need to upgrade your web app? RapidDev builds application with your growth in mind.

Best Practices for Building a Recruitment platform with Replit

Building a recruitment platform on Replit works best when you keep things simple, modular, and secured through Replit’s built-in tools. The most effective setup uses a Node.js + Express backend, a lightweight frontend (React or plain HTML/JS), and a small database connection (SQLite for local, or an external managed DB for production). Replit can manage your environment variables (through the “Secrets” tab), host full-stack applications, and even provide instant deployment, but you need to be organized — separate your code into backend and frontend directories, clearly manage configuration, and avoid storing secrets directly in code.

 

Recommended Project Structure

 

Inside your Replit project (“Repl”), create this folder layout:

  • index.js – main server file
  • frontend/ – all client-side files (HTML, CSS, JS, React if you use it)
  • database/ – for DB setup (like SQLite init scripts)
  • .env (or use Replit Secrets) – never commit local secrets; use Secrets tab instead

Example structure inside Replit sidebar:

index.js
frontend/
  index.html
  scripts.js
database/
  setup.js

 

Server Setup with Express

 

Install Express first in the Shell tab:

npm install express

Then, create index.js in the root of your Repl (or use the default main file). This runs your backend server:

const express = require('express')
const path = require('path')
const app = express()

app.use(express.json())

// Serve static files from frontend folder
app.use(express.static(path.join(__dirname, 'frontend')))

// Example API endpoint for job listings
app.get('/api/jobs', (req, res) => {
  // You'd normally fetch from DB
  const jobs = [{id: 1, title: 'Frontend Dev'}, {id: 2, title: 'Backend Dev'}]
  res.json(jobs)
})

// Example API endpoint for applications
app.post('/api/apply', (req, res) => {
  const { name, email, jobId } = req.body
  // Normally you'd store this in DB
  console.log('New application:', name, email, jobId)
  res.json({status: 'success'})
})

// Start server
app.listen(3000, () => {
  console.log('Server running on http://localhost:3000')
})

That code goes entirely in index.js. When you click “Run” in Replit, the platform automatically starts your server using this file, shows “Server running…” in Console, and generates a web URL for your app.

 

Static Frontend (Client UI)

 

In frontend/index.html you can create a simple form for users to apply to jobs:

<!DOCTYPE html>
<html>
<head>
  <title>Recruitment Platform</title>
</head>
<body>
  <h1>Available Jobs</h1>
  <div id="job-list"></div>

  <h2>Apply</h2>
  <form id="applyForm">
    <input type="text" id="name" placeholder="Full Name" required />
    <input type="email" id="email" placeholder="Email" required />
    <input type="number" id="jobId" placeholder="Job ID" required />
    <button type="submit">Submit</button>
  </form>

  <script src="scripts.js"></script>
</body>
</html>

And in frontend/scripts.js:

async function loadJobs() {
  const res = await fetch('/api/jobs')
  const jobs = await res.json()
  const jobList = document.getElementById('job-list')
  jobList.innerHTML = jobs.map(j => `<p>${j.id}. ${j.title}</p>`).join('')
}
loadJobs()

const form = document.getElementById('applyForm')
form.addEventListener('submit', async (e) => {
  e.preventDefault()
  const name = document.getElementById('name').value
  const email = document.getElementById('email').value
  const jobId = document.getElementById('jobId').value

  const res = await fetch('/api/apply', {
    method: 'POST',
    headers: {'Content-Type': 'application/json'},
    body: JSON.stringify({name, email, jobId})
  })
  const data = await res.json()
  alert(data.status === 'success' ? 'Application sent!' : 'Error sending application.')
})

These go into frontend/scripts.js and link from your index.html. Replit automatically serves them because you used express.static() in server code.

 

Using Database in Replit

 

If you want persistence, use SQLite (local) or a managed DB (like Supabase or MongoDB Atlas). For SQLite, install first:

npm install sqlite3

Create a new file database/setup.js:

const sqlite3 = require('sqlite3').verbose()
const db = new sqlite3.Database('./database/recruitment.db')

// Run once to create tables
db.serialize(() => {
  db.run('CREATE TABLE IF NOT EXISTS jobs (id INTEGER PRIMARY KEY, title TEXT)')
  db.run('CREATE TABLE IF NOT EXISTS applications (id INTEGER PRIMARY KEY, name TEXT, email TEXT, jobId INTEGER)')
  console.log('Database tables ready')
})

db.close()

Run it one time manually from Replit Shell:

node database/setup.js

Then, in index.js, import and use your database. Example modification to save an application:

const sqlite3 = require('sqlite3').verbose()
const db = new sqlite3.Database('./database/recruitment.db')

app.post('/api/apply', (req, res) => {
  const { name, email, jobId } = req.body
  db.run('INSERT INTO applications (name, email, jobId) VALUES (?, ?, ?)', [name, email, jobId], (err) => {
    if (err) {
      console.error(err)
      res.json({status: 'error'})
    } else {
      res.json({status: 'success'})
    }
  })
})

That snippet should replace your earlier dummy /api/apply endpoint inside index.js.

 

Using Secrets and Environment Variables

 

In Replit, never hard-code passwords, API keys, or DB credentials in your code. Go to Tools → Secrets, add variables like:

  • DB\_URL = your external database connection string
  • ADMIN\_TOKEN = secret token for admin routes

Then access them in your code:

const dbUrl = process.env.DB_URL

This keeps your sensitive info safe even when you share or collaborate on the Repl.

 

Deploying and Collaborating

 

Replit automatically hosts your app when you click “Run,” but for a more stable deployment, click Deploy in the sidebar. Choose “Static” if your site is just frontend, or “Always On” if you need backend APIs running constantly.

  • Enable “Always On” for production — so it doesn’t sleep after inactivity.
  • Use the “Invite” button to let other developers edit in real-time (great for collaboration).

 

Common Replit Pitfalls (and How to Avoid)

 

  • File Paths: Always use path.join(\_\_dirname, 'frontend') for serving assets — relative paths can break inside Replit runtimes.
  • Persistence: Replit sometimes resets filesystem state; use external DBs for permanent data.
  • Secrets: Don’t console.log() them; treat Replit Secrets like environment variables.
  • Package Versions: Lock important dependencies using package.json to avoid mismatched updates.

 

That’s a clean, real, and Replit-friendly stack for a recruitment platform — it’s fast to build, safe to run, and simple for teammates to join in and collaborate live.

Client trust and success are our top priorities

When it comes to serving you, we sweat the little things. That’s why our work makes a big impact.

Rapid Dev was an exceptional project management organization and the best development collaborators I've had the pleasure of working with. They do complex work on extremely fast timelines and effectively manage the testing and pre-launch process to deliver the best possible product. I'm extremely impressed with their execution ability.

CPO, Praction - Arkady Sokolov

May 2, 2023

Working with Matt was comparable to having another co-founder on the team, but without the commitment or cost. He has a strategic mindset and willing to change the scope of the project in real time based on the needs of the client. A true strategic thought partner!

Co-Founder, Arc - Donald Muir

Dec 27, 2022

Rapid Dev are 10/10, excellent communicators - the best I've ever encountered in the tech dev space. They always go the extra mile, they genuinely care, they respond quickly, they're flexible, adaptable and their enthusiasm is amazing.

Co-CEO, Grantify - Mat Westergreen-Thorne

Oct 15, 2022

Rapid Dev is an excellent developer for no-code and low-code solutions.
We’ve had great success since launching the platform in November 2023. In a few months, we’ve gained over 1,000 new active users. We’ve also secured several dozen bookings on the platform and seen about 70% new user month-over-month growth since the launch.

Co-Founder, Church Real Estate Marketplace - Emmanuel Brown

May 1, 2024 

Matt’s dedication to executing our vision and his commitment to the project deadline were impressive. 
This was such a specific project, and Matt really delivered. We worked with a really fast turnaround, and he always delivered. The site was a perfect prop for us!

Production Manager, Media Production Company - Samantha Fekete

Sep 23, 2022