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

Book a call with an Expert
Starting a new venture? Need to upgrade your web app? RapidDev builds application with your growth in mind.
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.
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.
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.
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.
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.
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.
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.
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.
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}`));
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}`));
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}`));

Book a call with an Expert
Starting a new venture? Need to upgrade your web app? RapidDev builds application with your growth in mind.
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.
Inside your Replit project (“Repl”), create this folder layout:
Example structure inside Replit sidebar:
index.js
frontend/
index.html
scripts.js
database/
setup.js
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.
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.
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.
In Replit, never hard-code passwords, API keys, or DB credentials in your code. Go to Tools → Secrets, add variables like:
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.
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.
path.join(\_\_dirname, 'frontend') for serving assets — relative paths can break inside Replit runtimes.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.
When it comes to serving you, we sweat the little things. That’s why our work makes a big impact.