/how-to-build-replit

How to Build a Blog backend with Replit

Learn how to build a powerful blog backend using Replit. Follow step-by-step guidance to create, deploy, and manage your blog with ease.

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 Blog backend with Replit

To build a simple yet real blog backend on Replit, create a Node.js + Express project, set up routes for posts, and connect it to a persistent database like Replit’s built-in SQLite. Replit automatically gives you a web URL for your Express app, so you can test API endpoints instantly. You’ll store secrets (like API keys) in the “Secrets” tab, organize the code into separate files (server, routes, and database logic), and make sure everything restarts cleanly when the server updates.

 

Step 1: Create a Node.js Repl

 

In Replit’s dashboard, click “+ Create Repl”, select “Node.js” template. Replit will generate index.js by default. This file is where your server code goes.

 

// index.js
const express = require("express");
const bodyParser = require("body-parser");
const Database = require("@replit/database"); // Simple key-value store built into Replit!

const app = express();
app.use(bodyParser.json());

const db = new Database(); // Connect to Replit DB

// Test route - you can visit this in browser
app.get("/", (req, res) => {
  res.send("Blog backend is running!");
});

// Start server
app.listen(3000, () => console.log("Server live on port 3000"));

 

Click “Run” — Replit boots the server and gives you a URL like https://your-repl-name.username.repl.co. That’s your live backend endpoint.

 

Step 2: Create a directory for routes

 

In your file tree on the left, right-click and select “New Folder” → name it `routes`. Inside it, create a new file called posts.js. This keeps your code modular.

 

// routes/posts.js
const express = require("express");
const router = express.Router();
const Database = require("@replit/database");
const db = new Database();

// Create a post
router.post("/", async (req, res) => {
  const { title, content } = req.body;
  if (!title || !content) return res.status(400).send("Missing title or content");
  
  const id = Date.now().toString(); // Unique ID based on timestamp
  await db.set(id, { id, title, content });
  res.send({ id, title, content });
});

// Get all posts
router.get("/", async (req, res) => {
  const keys = await db.list();
  const posts = [];
  for (let key of keys) {
    const post = await db.get(key);
    posts.push(post);
  }
  res.send(posts);
});

// Get single post
router.get("/:id", async (req, res) => {
  const post = await db.get(req.params.id);
  if (!post) return res.status(404).send("Post not found");
  res.send(post);
});

// Delete post
router.delete("/:id", async (req, res) => {
  await db.delete(req.params.id);
  res.send({ message: "Post deleted" });
});

module.exports = router;

 

Step 3: Connect routes to your server

 

Now open index.js again and import that route file.

 

// index.js (add this near the top)
const postsRoute = require("./routes/posts");

// Below your existing middleware (after app.use(bodyParser.json()))
app.use("/api/posts", postsRoute);

 

This means that when you visit /api/posts in the browser or with a tool like Postman, your new routes will respond. For example:

  • GET /api/posts → get all blog posts
  • POST /api/posts → create new post (send JSON: { title, content })
  • GET /api/posts/:id → fetch post by ID
  • DELETE /api/posts/:id → remove a post

 

Step 4: Store secrets safely

 

If later you want to connect to an external database (like MongoDB), go to the “Secrets” (🔒 icon on left sidebar) and add your MONGODB\_URI variable instead of writing it directly into the file. Then access it as:

 

const mongoUri = process.env.MONGODB_URI;

 

This avoids exposing passwords in your code or public Repls.

 

Step 5: Understand Replit behavior

 

  • Auto-restarts: Replit restarts your server every time you change code. That’s normal — just save often.
  • Package installs: When you type require(), Replit may prompt to auto-install; say yes or run npm install express body-parser @replit/database in shell.
  • Persistent data: Replit DB persists between runs, but deleting the Repl or renaming it can lose data. For production, migrate to an external DB.
  • Testing endpoints: Use the “Webview” pane on right or external tools like curl/Postman for easier debugging.

 

Step 6: Deploying properly

 

Replit projects stay live while the tab is open. For a semi-permanent setup, use Replit’s “Always On” feature (Pro accounts) or connect to an external uptime monitor (like UptimeRobot hitting your main endpoint to keep it alive).

 

Summary

 

You’ve got a working backend that supports basic CRUD (Create, Read, Delete) operations for blog posts using Express and Replit’s built-in database. The key idea is to organize your server entry point (index.js), define API routes in routes/posts.js, and let Replit’s URL serve as your live endpoint. This is a stable, realistic way to build a blog backend right inside Replit’s free or pro environment — no fake tools, just real working components.

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 and Manage Blog Posts with Express and Replit Database


const express = require('express');
const Database = require('@replit/database');
const { v4: uuidv4 } = require('uuid');

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

app.use(express.json());

// Create or update a blog post
app.post('/api/posts', async (req, res) => {
  const { id, title, content, author } = req.body;
  if (!title || !content || !author) {
    return res.status(400).json({ error: 'Missing required fields' });
  }

  const postId = id || uuidv4();
  const timestamp = new Date().toISOString();
  const post = { id: postId, title, content, author, updatedAt: timestamp };

  await db.set(`post:${postId}`, post);
  res.json({ message: 'Post saved', post });
});

// Fetch all posts
app.get('/api/posts', async (req, res) => {
  const keys = await db.list('post:');
  const posts = await Promise.all(keys.map(k => db.get(k)));
  posts.sort((a, b) => new Date(b.updatedAt) - new Date(a.updatedAt));
  res.json(posts);
});

// Fetch a single post
app.get('/api/posts/:id', async (req, res) => {
  const post = await db.get(`post:${req.params.id}`);
  if (!post) return res.status(404).json({ error: 'Post not found' });
  res.json(post);
});

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => console.log(`Server running at http://localhost:${PORT}`));

How to Build a Blog Post Publisher API with Express and Replit


const express = require('express');
const fetch = require('node-fetch');
const bodyParser = require('body-parser');
require('dotenv').config();

const app = express();
app.use(bodyParser.json());

app.post('/api/publish', async (req, res) => {
  const { title, content } = req.body;
  if (!title || !content) {
    return res.status(400).json({ error: 'Title and content required' });
  }

  try {
    const response = await fetch('https://api.hashnode.com/', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': process.env.HASHNODE\_TOKEN
      },
      body: JSON.stringify({
        query: \`
          mutation CreateStory {
            createStory(
              input: {
                title: "${title}",
                contentMarkdown: """${content}"""
                tags: [{ \_id: "56744723958ef13879b9549b" }]
              }
            ) {
              post { slug publication { domain } }
            }
          }
        \`
      })
    });

    const result = await response.json();
    if (result.errors) {
      return res.status(500).json({ error: 'Failed to publish', details: result.errors });
    }

    const postUrl = `${result.data.createStory.post.publication.domain}/${result.data.createStory.post.slug}`;
    res.json({ message: 'Post published successfully', url: postUrl });
  } catch (err) {
    res.status(500).json({ error: 'Server error', details: err.message });
  }
});

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

How to Add Image Uploads and Post Management to Your Blog Backend on Replit


const express = require('express');
const { v4: uuidv4 } = require('uuid');
const Database = require('@replit/database');
const multer = require('multer');
const path = require('path');
const fs = require('fs');

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

// Configure multer for image uploads (for blog cover images)
const upload = multer({
  dest: 'uploads/',
  limits: { fileSize: 3 _ 1024 _ 1024 }, // limit to 3MB
  fileFilter: (req, file, cb) => {
    const ext = path.extname(file.originalname).toLowerCase();
    if (ext !== '.png' && ext !== '.jpg' && ext !== '.jpeg') {
      return cb(new Error('Only images are allowed'));
    }
    cb(null, true);
  }
});

app.use(express.json());
app.use('/uploads', express.static('uploads'));

// Save post metadata + image reference in database
app.post('/api/posts/with-image', upload.single('cover'), async (req, res) => {
  const { title, content, author } = req.body;
  if (!title || !content || !author) {
    return res.status(400).json({ error: 'Missing required fields' });
  }

  const postId = uuidv4();
  const imagePath = req.file ? `/uploads/${req.file.filename}` : null;
  const post = {
    id: postId,
    title,
    content,
    author,
    coverImage: imagePath,
    createdAt: new Date().toISOString()
  };

  await db.set(`post:${postId}`, post);
  res.json({ message: 'Post created', post });
});

// Clean up old images when deleting posts
app.delete('/api/posts/:id', async (req, res) => {
  const id = req.params.id;
  const post = await db.get(`post:${id}`);
  if (!post) return res.status(404).json({ error: 'Post not found' });

  if (post.coverImage) {
    const localPath = path.join(\_\_dirname, post.coverImage);
    if (fs.existsSync(localPath)) fs.unlinkSync(localPath);
  }

  await db.delete(`post:${id}`);
  res.json({ message: 'Post deleted' });
});

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => console.log(`Blog backend running on http://localhost:${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 Blog backend with Replit

A solid and realistic way to build a blog backend on Replit is to create a small Node.js and Express app that exposes routes (for example /posts to fetch blog posts, /admin to add new posts). You store posts in a simple SQLite database (which fits perfectly in Replit) or in a JSON file if you’re just starting. You’ll use Replit’s built-in “Secrets” system for any API keys or database credentials, and Replit’s “Always On” or external deployment (like using Replit Deployments or hosting elsewhere) if you want the backend reachable 24/7. The best practice is to keep your server code organized: one server.js to start the app, one routes/ folder for API endpoints, and one db.js for your database logic. Avoid expecting Replit’s ephemeral filesystem to keep data safe across restarts — always persist important data in SQLite or an external database.

 

Step-by-step Setup

 

Create a new Repl: choose the “Node.js” template. Replit will make a default index.js. Rename it to server.js for clarity.

  • In sidebar, click the “Add file” button and create a server.js.
  • Delete or ignore index.js after renaming if it exists.

 

Install dependencies

 

npm install express sqlite3 cors

 

Explanation:

  • express – web framework for routes
  • sqlite3 – lightweight local database engine (perfect for Replit)
  • cors – enables your frontend to call your backend safely

 

Basic Express server (in server.js)

 

// server.js
const express = require('express')
const cors = require('cors')
const app = express()
const PORT = process.env.PORT || 3000

app.use(cors())
app.use(express.json()) // allows JSON bodies in requests

app.get('/', (req, res) => {
  res.send('Blog backend is running!')
})

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

Click the green “Run” button on Replit, then open the Replit webview link. You’ll see “Blog backend is running!”. That’s your basic backend.

 

Add a Database File

 

Add a new file named db.js in the root of your Repl. This will handle SQLite connections. A database.db file will be created automatically when you first run the app.

 

// db.js
const sqlite3 = require('sqlite3').verbose()

// Create or open the database
const db = new sqlite3.Database('./database.db', (err) => {
  if (err) console.error('Database failed to open', err)
  else console.log('Connected to SQLite database')
})

// Create a table for posts if it doesn't exist
db.run(`CREATE TABLE IF NOT EXISTS posts (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  title TEXT,
  content TEXT,
  created_at DATETIME DEFAULT CURRENT_TIMESTAMP
)`)

module.exports = db

 

Create API Routes

 

Add a new folder named routes and inside it, create a file called posts.js. You’ll separate your routes — this keeps server.js clean.

 

// routes/posts.js
const express = require('express')
const router = express.Router()
const db = require('../db')

// GET all posts
router.get('/', (req, res) => {
  db.all('SELECT * FROM posts ORDER BY created_at DESC', [], (err, rows) => {
    if (err) return res.status(500).json({ error: err.message })
    res.json(rows)
  })
})

// POST a new blog post
router.post('/', (req, res) => {
  const { title, content } = req.body
  if (!title || !content) return res.status(400).json({ error: 'Missing fields' })
  db.run('INSERT INTO posts (title, content) VALUES (?, ?)', [title, content], function(err) {
    if (err) return res.status(500).json({ error: err.message })
    res.json({ id: this.lastID })
  })
})

module.exports = router

 

Connect Routes to the Server

 

Go back to server.js and mount your routes:

 

// server.js
const express = require('express')
const cors = require('cors')
const app = express()
const PORT = process.env.PORT || 3000

app.use(cors())
app.use(express.json())

// Import and use the posts router
const postsRouter = require('./routes/posts')
app.use('/posts', postsRouter)

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

 

Use Replit Secrets

 

For any sensitive data (API keys, database URLs, admin passwords), never hardcode them in files. Go to the Replit sidebar → “Secrets” (lock icon), add new keys (for example ADMIN\_TOKEN), and access them in your code like this:

 

const adminToken = process.env.ADMIN_TOKEN

 

Common Replit Gotchas

 

  • Replit restarts Repls – SQLite data persists within the Repl’s filesystem, but not guaranteed forever. For production-level projects, back up your database.db or switch to a remote DB (like Supabase).
  • Always use Replit Secrets – Never add secret tokens or passwords directly into your code files.
  • Don’t rely on Replit file watchers for heavy tasks – keep scripts and DB initialization lightweight so it starts fast.
  • Use proper folder structureroutes/, db.js, and server.js separation helps clarity and easier debugging.
  • Enable “Always On” or Deploy in Replit if you want API accessible continuously. Otherwise, the Repl sleeps after inactivity.

 

Testing Your Blog Backend

 

Use your browser or a tool like curl or Postman.

 

curl -X POST https://<your-repl-name>.<your-username>.repl.co/posts \
  -H "Content-Type: application/json" \
  -d '{"title": "My first post", "content": "Hello from Replit!"}'

 

Then open /posts from the Replit Webview to see the stored data. You now have a functional, persistent blog backend built the clean way inside Replit.

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