/how-to-build-replit

How to Build a Resume builder backend with Replit

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

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 Resume builder backend with Replit

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.

 

Step 1: Create the Repl and Setup the Environment

 

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.

  • In the Replit left sidebar, click “+” and create a new file called server.js.
  • In the Shell at the bottom, run the following command to install Express (the framework for building HTTP servers):
npm install express cors
  • express helps you create API endpoints.
  • cors allows your frontend (in another Repl or local) to safely call your backend.

 

Step 2: Create the Basic Server

 

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!”.

 

Step 3: Add API Endpoints for Resume Data

 

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);
});
  • POST /api/resume lets your front-end send data (like name, education, skills, etc.).
  • GET /api/resume lets you fetch everything that has been saved (while the server is running).
  • This data disappears when the Repl restarts — for persistent storage, you’ll add a database next.

 

Step 4: (Optional but Recommended) Connect a Database

 

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.

 

Step 5: Handling Secrets (Optional)

 

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.

 

Step 6: Collaboration and Deployment Tips

 

  • Use the “Invite” button on the top right for real-time coding with teammates.
  • Replit automatically hosts your backend using the provided domain (it starts instantly when someone visits).
  • Be aware that free Repls go to “sleep” when inactive. For live products, consider Replit’s “Always On” option or external deployment (like Render or Railway).
  • Use Replit’s built-in Git integration (bottom-left sidebar) to connect with GitHub for version control.

 

Step 7: Connect Your Frontend

 

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.

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 Create a Resume Builder API with Express and Replit Database


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"));

How to Add PDF Export Functionality to Your Resume Builder Backend on Replit


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"));

How to Add Profile Photo Uploads to Your Resume Builder Backend


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"));

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 Resume builder backend with Replit

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.

 

1. Set Up Your Project Structure

 

In your Replit project (Node.js template), create the following structure:

  • index.js – main entry point (Replit runs this automatically)
  • /routes – folder for Express route files, e.g. user routes or resume routes
  • /models – folder for data layer or mock database code
  • /utils – folder for helpers like input validation

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

 

2. Create the Express Server (index.js)

 

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.

 

3. Add Routes (create /routes/resumeRoutes.js)

 

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.

 

4. Use Secrets for Environment Variables

 

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.

 

5. Testing and Debugging on Replit

 

  • Preview automatically: When your server runs, Replit shows a preview window with your deployed server URL. Use it to test your endpoints.
  • 🟢 Always check the console: If you get “address in use” or “cannot find module,” stop the run and re-launch. Replit sometimes holds old processes.
  • Logs persist: Use console.log() in route files — it appears in the console immediately, helping debug input data.

 

6. Collaboration & Multiplayer Tips

 

  • If teammates join your Repl, only one person should run the server; others can edit files — Replit syncs live.
  • To avoid merge headaches, connect Repl to GitHub under Version Control (left toolbar), so commits track all changes properly.

 

7. Deployment Note

 

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.

 

8. Common Pitfalls

 

  • Do not rely on local file writes for long-term storage (like JSON files) — when Replit restarts, these can reset. Use SQLite or an external DB instead.
  • Do not hardcode secrets (like API keys); store them in Replit’s Secrets.
  • Use console logs and test routes frequently to confirm everything works before adding complexity like authentication or file uploads.

 

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.

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