Learn how to create a powerful resume builder backend using Replit. Follow simple steps to build, deploy, and scale your web app efficiently.

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 backend for a Resume Builder on Replit, you can create a simple Node.js + Express API project that handles form data (like name, skills, experience), stores it (temporarily in memory or in a small database), and provides endpoints for your front-end (e.g. React, plain HTML/JS, etc.) to interact with. In Replit, that means creating a new Node.js Repl, setting up a `server.js` file, installing dependencies with `npm`, and using Replit’s built-in web server and Secrets to keep things safe. The backend can serve JSON data to your front-end so you can preview and manage resumes dynamically.
Create a new Repl using the “Node.js” template. Replit will automatically give you default files like index.js (you can rename it to server.js for clarity) and a package.json.
npm install express cors
Open server.js and add this code:
// Import necessary packages
const express = require('express');
const cors = require('cors');
// Create the app
const app = express();
// Middleware to parse incoming JSON requests
app.use(express.json());
// Enable CORS for all routes
app.use(cors());
// Define server port (Replit provides process.env.PORT)
const PORT = process.env.PORT || 3000;
// A simple test route
app.get('/', (req, res) => {
res.send('Resume Builder Backend is running!');
});
// Start the server on the given port
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
You can now press the Run button (green triangle at the top). Replit will show a new web window — that’s your live backend URL. Visiting it should show: “Resume Builder Backend is running!”.
Now, let’s simulate storing resume information in memory. Open server.js and add the following code after your test route, right above app.listen():
// Temporary in-memory "database"
let resumes = [];
// POST endpoint: Save resume data
app.post('/api/resume', (req, res) => {
const newResume = req.body; // Get data sent from frontend
newResume.id = resumes.length + 1;
resumes.push(newResume);
res.status(201).json({ message: 'Resume saved successfully!', resume: newResume });
});
// GET endpoint: Fetch all resumes
app.get('/api/resume', (req, res) => {
res.json(resumes);
});
For a real Resume Builder, you’ll want to keep data after Replit sleeps or restarts. The simplest built-in option is Replit Database (a key-value store). Install it using this command:
npm install @replit/database
At the top of your server.js, import and set up the database:
const Database = require('@replit/database');
const db = new Database();
Then, replace your temporary in-memory logic with:
// Save resume to Replit Database
app.post('/api/resume', async (req, res) => {
const data = req.body;
const id = Date.now(); // unique id
await db.set(id.toString(), data);
res.status(201).json({ message: 'Saved successfully', id });
});
// Get all resumes from the database
app.get('/api/resume', async (req, res) => {
const keys = await db.list();
const allResumes = [];
for (const key of keys) {
const resume = await db.get(key);
allResumes.push({ id: key, ...resume });
}
res.json(allResumes);
});
This way, your resumes persist across runs (within Replit’s limits). The database stores small JSON-like objects — that’s perfect for hobby projects like this.
If later you add external APIs (like for PDF generation or emailing), store API keys in Replit’s “Secrets” tab — NOT directly in your code. You can read them using process.env.SECRET\_NAME.
If you’re building the frontend (for example with React on another Repl), call your backend API using fetch() with the backend URL — which you can get from the “Open in new tab” icon next to the running Repl preview:
// Example fetch call from frontend
fetch('https://your-backend-name.your-username.repl.co/api/resume')
.then(res => res.json())
.then(data => console.log(data));
That’s your full, working backend for a Resume Builder on Replit — it’s lightweight, real, and expandable. You can add authentication later, generate PDFs, or link it with a React interface, all while working inside Replit’s collaborative, browser-based setup.
import express from "express";
import { v4 as uuidv4 } from "uuid";
import Database from "@replit/database";
const app = express();
const db = new Database();
app.use(express.json());
// Schema for a resume object
// { id, userId, basics, experience[], education[], skills[] }
app.post("/api/resumes", async (req, res) => {
const userId = req.headers["x-user-id"];
if (!userId) return res.status(401).json({ error: "Missing user ID" });
const newResume = {
id: uuidv4(),
userId,
basics: req.body.basics || {},
experience: req.body.experience || [],
education: req.body.education || [],
skills: req.body.skills || [],
createdAt: new Date().toISOString()
};
await db.set(`resume:${userId}:${newResume.id}`, newResume);
res.status(201).json(newResume);
});
app.get("/api/resumes/:id", async (req, res) => {
const userId = req.headers["x-user-id"];
const id = req.params.id;
const resume = await db.get(`resume:${userId}:${id}`);
if (!resume) return res.status(404).json({ error: "Resume not found" });
res.json(resume);
});
app.put("/api/resumes/:id", async (req, res) => {
const userId = req.headers["x-user-id"];
const id = req.params.id;
const key = `resume:${userId}:${id}`;
const existing = await db.get(key);
if (!existing) return res.status(404).json({ error: "Resume not found" });
const updated = {
...existing,
...req.body,
updatedAt: new Date().toISOString()
};
await db.set(key, updated);
res.json(updated);
});
app.delete("/api/resumes/:id", async (req, res) => {
const userId = req.headers["x-user-id"];
const id = req.params.id;
await db.delete(`resume:${userId}:${id}`);
res.status(204).end();
});
app.listen(3000, () => console.log("Resume builder backend running on port 3000"));
import express from "express";
import fetch from "node-fetch";
import Database from "@replit/database";
const app = express();
const db = new Database();
app.use(express.json());
// Generate a public resume PDF using an external API (like a PDF rendering service)
app.post("/api/resumes/:id/export", async (req, res) => {
const { id } = req.params;
const userId = req.headers["x-user-id"];
if (!userId) return res.status(401).json({ error: "Missing user ID" });
const resume = await db.get(`resume:${userId}:${id}`);
if (!resume) return res.status(404).json({ error: "Resume not found" });
try {
const response = await fetch("https://api.pdfmonkey.io/api/v1/documents", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${process.env.PDFMONKEY_API_KEY}`,
},
body: JSON.stringify({
template_id: process.env.PDFMONKEY_TEMPLATE\_ID,
data: resume
}),
});
const result = await response.json();
if (!response.ok) throw new Error(result.errors || "PDF generation failed");
// Store generated PDF URL for later retrieval
resume.pdfUrl = result.data.attributes.download\_url;
await db.set(`resume:${userId}:${id}`, resume);
res.json({ pdfUrl: resume.pdfUrl });
} catch (err) {
console.error("PDF export failed:", err);
res.status(500).json({ error: "Failed to generate PDF" });
}
});
app.listen(3000, () => console.log("Resume export service running"));
import express from "express";
import multer from "multer";
import Database from "@replit/database";
import { v4 as uuidv4 } from "uuid";
import path from "path";
import fs from "fs";
const app = express();
const db = new Database();
// Configure multer to store files in uploads/
const upload = multer({ dest: "uploads/" });
// Endpoint to upload profile photo and attach it to resume object
app.post("/api/resumes/:id/photo", upload.single("photo"), async (req, res) => {
try {
const userId = req.headers["x-user-id"];
const { id } = req.params;
if (!userId) return res.status(401).json({ error: "Missing user ID" });
const resumeKey = `resume:${userId}:${id}`;
const resume = await db.get(resumeKey);
if (!resume) return res.status(404).json({ error: "Resume not found" });
// Rename file for uniqueness and move it to a stable path
const ext = path.extname(req.file.originalname);
const newFilename = `${uuidv4()}${ext}`;
const newPath = path.join("uploads", newFilename);
fs.renameSync(req.file.path, newPath);
// Store the file path as part of resume profile
resume.profilePhoto = `/${newPath}`;
resume.updatedAt = new Date().toISOString();
await db.set(resumeKey, resume);
res.json({ message: "Photo uploaded successfully", profilePhoto: resume.profilePhoto });
} catch (err) {
console.error("Photo upload failed:", err);
res.status(500).json({ error: "Server error while uploading photo" });
}
});
// Serve uploaded photos as static files
app.use("/uploads", express.static("uploads"));
app.listen(3000, () => console.log("Resume photo upload service running on port 3000"));

Book a call with an Expert
Starting a new venture? Need to upgrade your web app? RapidDev builds application with your growth in mind.
For building a Resume Builder backend on Replit, use a simple and reliable stack: Node.js with Express.js for the server, Replit Secrets for credentials, and either a lightweight SQLite database (via `better-sqlite3`) or an external DB like MongoDB Atlas. Organize your code using clear folders like /routes, /models, and /utils. Run your server using index.js at the project root. This setup works very well within Replit’s environment, supports collaboration, and minimizes issues with long-running processes.
In your Replit project (Node.js template), create the following structure:
You can create new folders by right-clicking on the file tree in Replit → “New Folder.”
npm init -y
npm install express better-sqlite3 cors
Open (or create) index.js in the root of your project. This file starts your backend.
// index.js
import express from "express"
import cors from "cors"
import Database from "better-sqlite3"
const app = express()
app.use(cors())
app.use(express.json()) // allows JSON request body parsing
// Setup SQLite (local file stored in your Replit project)
const db = new Database("resumes.db")
// Create table if not exists
db.prepare(`
CREATE TABLE IF NOT EXISTS resumes (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT,
email TEXT,
experience TEXT,
education TEXT
)
`).run()
// Basic test route
app.get("/", (req, res) => {
res.send("Resume Builder Backend Running 🚀")
})
// Import route handlers
import resumeRoutes from "./routes/resumeRoutes.js"
app.use("/api/resumes", resumeRoutes)
// Start server (Replit exposes PORT automatically)
const PORT = process.env.PORT || 3000
app.listen(PORT, () => console.log(`Server running on port ${PORT}`))
You should put this code exactly in your index.js. This is where the app starts when you click “Run” in Replit.
Inside the /routes folder, create resumeRoutes.js. This file holds the logic for saving and retrieving resumes.
// /routes/resumeRoutes.js
import express from "express"
import Database from "better-sqlite3"
const router = express.Router()
const db = new Database("resumes.db")
// Save a new resume
router.post("/", (req, res) => {
const { name, email, experience, education } = req.body
if (!name || !email) {
return res.status(400).json({ error: "Name and email are required." })
}
db.prepare("INSERT INTO resumes (name, email, experience, education) VALUES (?, ?, ?, ?)")
.run(name, email, experience, education)
res.json({ message: "Resume saved successfully!" })
})
// Get all resumes
router.get("/", (req, res) => {
const resumes = db.prepare("SELECT * FROM resumes").all()
res.json(resumes)
})
export default router
This code uses SQLite inside your Replit project (a simple database file). You can easily adapt it to MongoDB if you prefer external storage.
If you later add authentication or connect to an external database (like MongoDB), store credentials safely. In Replit, open “Secrets” tab (lock icon on left bar), add keys like MONGO_URI or JWT_SECRET. Access them in your code as:
const mongoUri = process.env.MONGO_URI
This keeps sensitive information from being visible in your code or public repos.
console.log() in route files — it appears in the console immediately, helping debug input data.
Replit’s “Always On” is useful for keeping backend running (for Pro users). For free accounts, it will sleep after inactivity, but that’s fine for a portfolio or prototype. You can always export to another service later (like Render or Fly.io) while developing comfortably on Replit.
This approach gives you a clean, deployable, and realistic backend foundation that plays well with Replit’s strengths — instant environment, simple database integration, and collaboration support — without the frustration of unexpected resets or blocked network calls.
When it comes to serving you, we sweat the little things. That’s why our work makes a big impact.