The Silent Server: A Node.js Debugging Story A seemingly successful server startup that hides a critical mistake

nikhil kumarnikhil kumar
4 min read

The Mystery

Picture this: You're working on your Node.js backend, everything seems to be working perfectly. Your terminal shows:

MongoDB connected ! DB host:ac-a3waqux-shard-00-00.wgyfwou.mongodb.net 
Server is running on port 8000

But when you try to test your API endpoints with Postman or curl, you get hit with:

Error: connect ECONNREFUSED 127.0.0.1:8000

Frustrating, right? The server claims it's running, MongoDB is connected, but somehow your requests are being refused. This is exactly what happened to me while building my VidTube project, and the solution was both simple and enlightening.

The Investigation

When faced with ECONNREFUSED, the usual suspects are:

  • Firewall blocking the port ๐Ÿ”ฅ

  • Wrong port configuration ๐Ÿ”Œ

  • Route registration issues ๐Ÿ›ฃ๏ธ

  • Middleware blocking requests ๐Ÿšซ

I checked all of these. My routes looked correct:

// healthcheck.controller.js
const healthcheck = asyncHandler(async(req,res) => {
    console.log("in health check ");
    return res
        .status(200)
        .json(new ApiResponse(200,"Ok","Health check passed"))
})

The port seemed right, firewall was off, and middleware looked fine. So what was wrong?

The Eureka Moment

The issue was hiding in plain sight in my src/index.js file:

const port = process.env.PORT || 9000;

connectDB()
.then(() => {
    console.log(`Server is running on port ${port}`);
    // ๐Ÿšจ WAIT... WHERE IS app.listen()? ๐Ÿšจ
})
.catch((err) => {
    console.log(`MongoDB connection error ${err}`);
})

I was logging "Server is running" but never actually starting the HTTP server!

The Fix

The solution was embarrassingly simple:

const port = process.env.PORT || 9000;

connectDB()
.then(() => {
    // Actually start the HTTP server!
    app.listen(port, () => {
        console.log(`Server is running on port ${port}`);
    });
})
.catch((err) => {
    console.log(`MongoDB connection error ${err}`);
})

Why This Happens

This is a classic case of misleading logging. My code was:

  1. โœ… Successfully connecting to MongoDB

  2. โœ… Logging a success message

  3. โŒ Forgetting to start the actual HTTP server

The confusion comes from thinking that importing and configuring Express automatically starts the server. It doesn't! You need to explicitly call app.listen() to bind the server to a port and start accepting HTTP requests.

The Learning

This bug taught me several important lessons:

1. Logs Should Reflect Reality

Don't log "Server is running" until the server is actually listening for connections:

app.listen(port, () => {
    // Only log this when the server is truly ready
    console.log(`โœ… Server is listening on port ${port}`);
});

2. Database โ‰  HTTP Server

Connecting to a database and starting an HTTP server are two separate operations. Just because one succeeds doesn't mean the other has happened.

3. Test Your Assumptions

When debugging, don't assume the obvious things are working. Sometimes the most basic step is the one you missed.

4. Use Better Debug Tools

Commands like netstat -an | findstr :8000 (Windows) or lsof -i :8000 (Mac/Linux) can quickly tell you if anything is actually listening on your port.

Prevention Tips

To avoid this in future projects:

  1. Structure your server startup clearly:

     const startServer = async () => {
         try {
             await connectDB();
             console.log('โœ… Database connected');
    
             app.listen(port, () => {
                 console.log(`โœ… Server listening on port ${port}`);
             });
         } catch (error) {
             console.error('โŒ Server startup failed:', error);
             process.exit(1);
         }
     };
    
     startServer();
    
  2. Add a health check that actually works:

     // Test your server immediately after startup
     setTimeout(() => {
         fetch(`http://localhost:${port}/api/v1/healthcheck`)
             .then(() => console.log('โœ… Health check passed'))
             .catch(() => console.log('โŒ Health check failed'));
     }, 1000);
    
  3. Use process managers in development: Tools like nodemon are great, but consider using PM2 which gives you better insights into whether your processes are actually running.

Conclusion

Sometimes the biggest bugs are the smallest oversights. This "silent server" taught me to be more careful about what my logs actually represent and to always verify that my assumptions match reality.

The next time you see ECONNREFUSED but your logs claim everything is fine, check if you're actually calling app.listen(). You might be surprised!


Have you encountered similar "obvious" bugs that took forever to debug? Share your stories in the comments below!

Tags: #nodejs #debugging #express #javascript #webdevelopment #backend

1
Subscribe to my newsletter

Read articles from nikhil kumar directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

nikhil kumar
nikhil kumar