Back to blog
Mar 07, 2025
4 min read

Build a Self-Destructing Message App with Express.js & MongoDB

Learn to build a secure self-destructing message app using Express.js and MongoDB. Step-by-step guide with code examples for developers.

Ephemeral messaging is booming, with users prioritizing privacy. In this tutorial, you’ll create a self-destructing message app that automatically deletes messages after they’re viewed. Perfect for developers seeking to enhance their full-stack skills while addressing real-world privacy concerns.

Why Build a Self-Destructing Message App?

  • Privacy Demand: Users want control over sensitive data.
  • Skill Boost: Master Express.js routing, MongoDB TTL indexes, and frontend integration.
  • Portfolio Potential: Stand out with a unique project that showcases security awareness.

Build a 24-Hour Expiring URL Shortener with Node.js & MongoDB

Prerequisites

Step 1: Project Setup

Initialize Express.js

mkdir self-destruct-app && cd self-destruct-app
npm init -y
npm install express mongoose dotenv

Folder Structure

├── models/Message.js
├── routes/messages.js
├── views/ (for frontend)
├── app.js
└── .env

Express.js Without Routing: Handling Everything in a Single Middleware

Step 2: Configure MongoDB with TTL Indexes

MongoDB’s TTL (Time-To-Live) feature automatically deletes documents after a set time.

  1. Create a Message Schema (models/Message.js):
const messageSchema = new mongoose.Schema({
  content: { type: String, required: true },
  expiresAt: { type: Date, default: Date.now, index: { expires: '1m' } }
});
  1. Set Expiration Logic:
    The expires property (e.g., '1m' for 1 minute) determines when the message self-destructs.

Step 3: Create API Endpoints

POST Route to Save Messages

// routes/messages.js
router.post('/', async (req, res) => {
  try {
    const newMessage = await Message.create({ content: req.body.content });
    res.json({ id: newMessage._id, expiresAt: newMessage.expiresAt });
  } catch (error) {
    res.status(500).send('Error saving message');
  }
});

GET Route to Retrieve and Delete Messages

router.get('/:id', async (req, res) => {
  try {
    const message = await Message.findById(req.params.id);
    if (!message) return res.status(404).send('Message expired or not found');
    
    await Message.deleteOne({ _id: req.params.id }); // Immediate deletion after fetch
    res.json({ content: message.content });
  } catch (error) {
    res.status(500).send('Error retrieving message');
  }
});

Step 4: Build the Frontend

Create a simple HTML/JS interface (views/index.html):

<form id="messageForm">
  <textarea id="content" required></textarea>
  <button type="submit">Create Self-Destruct Link</button>
</form>

<script>
  document.getElementById('messageForm').addEventListener('submit', async (e) => {
    e.preventDefault();
    const response = await fetch('/messages', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ content: e.target.content.value })
    });
    const { id } = await response.json();
    alert(`Share this link (expires in 1 minute): ${window.location.href}${id}`);
  });
</script>

Step 5: Test Your App

  1. Start the server: node app.js
  2. Visit http://localhost:3000, submit a message, and share the generated link.
  3. When the recipient opens the link, the message displays once and then deletes.

Security Best Practices

  • HTTPS: Deploy with HTTPS to encrypt data in transit.
  • Input Sanitization: Use libraries like express-validator to prevent XSS attacks.
  • Rate Limiting: Block brute-force attacks with express-rate-limit.

Use Cases for Your App

  • Sensitive Data Sharing: Passwords, confidential documents.
  • Ephemeral Notifications: Time-bound alerts or updates.
  • Privacy-First Chat: Add real-time features with Socket.io.

Troubleshooting Common Issues

  • TTL Not Working: Ensure MongoDB’s TTL worker is running (runs every 60 seconds).
  • CORS Errors: Use the cors middleware if hosting frontend separately.
  • Deletion Failures: Add error logging to your GET route.

Conclusion

You’ve built a self-destructing message app that combines Express.js for backend logic, MongoDB for automated data deletion, and a minimalist frontend. This project demonstrates your ability to solve privacy-centric challenges—a valuable addition to your developer portfolio.

Next Steps:

  • Add user authentication with Passport.js
  • Implement end-to-end encryption using crypto
  • Extend expiration time options (e.g., 5 minutes, 1 hour)