Sitemap

Understanding When to use return res.json() in Express

3 min readApr 12, 2025

--

Press enter or click to view image in full size

When writing Express applications, it’s common to send JSON responses using res.json(). But should you return it? And does it really matter?

Yes — it does.

In this article, we’ll explore why using return res.json(...) is sometimes essential for clean, correct behavior, and when it’s safe to skip the return.

Why This Matters

Express doesn’t stop executing the function after res.json(...) is called. It only sends the response — the code after it still runs unless you explicitly use return.

If you forget to return:

  • You may accidentally send multiple responses (causing a crash)
  • Expensive operations like DB queries or computations might still execute
  • Logs or other code may run unnecessarily, wasting resources

When You Should Use return res.json(...)

Anytime you need to exit early or conditionally stop the request lifecycle, use return res.json(...).

Example: Input Validation with Early Exit

app.get('/user/:id', async (req, res) => {
const id = parseInt(req.params.id);

if (isNaN(id)) {
return res.status(400).json({ error: 'Invalid ID' });
}

const user = await getUserById(id);

if (!user) {
return res.status(404).json({ error: 'User not found' });
}

res.json(user);
});

Why This Works:

  • Each return guarantees that no additional code runs after the response is sent.
  • Without it, the route could try to execute logic or send another response, causing errors or waste.

When It’s Safe to Omit return

If res.json(...) is the last line of your route handler and there's no further logic, then you don’t need return.

Example:

app.get('/ping', (req, res) => {
res.json({ status: 'ok' }); // no return needed here
});

Why It’s Okay:

  • Nothing comes after the response.
  • No risk of unintentional processing or multiple responses.

What Happens If You Forget return But Continue Code?

This is one of the most common mistakes developers make.

Problematic Example:

app.get('/check', (req, res) => {
if (!req.query.token) {
res.status(401).json({ error: 'Missing token' }); // ❌ No return here
}

doSomethingExpensive(); // ❌ Still runs even after response is sent

res.json({ status: 'ok' }); // ❌ Will crash with "headers already sent"
});

What Goes Wrong:

  • The request already received a response (401), but the function didn’t stop.
  • More code ran, including another res.json(...) call.
  • Result: ❌ “Can’t set headers after they are sent” error.

Correct Version:

if (!req.query.token) {
return res.status(401).json({ error: 'Missing token' });
}

Bonus: Async Route Handlers Still Need Return for Early Exits

Even in async functions, the return makes a difference when you want to exit early.

Example:

app.get('/async', async (req, res) => {
if (!req.query.id) {
return res.status(400).json({ error: 'ID is required' });
}

const data = await getData(req.query.id);

res.json(data); // ✅ return optional here - it's the last line
});
  • Here, return isn’t needed for the last res.json(...).
  • But the early exit on missing id still needs a return.

Rule of Thumb

If you’re not done, use return res.json(...). If you’re done and it’s the last line, just res.json(...) is fine.

This simple habit can prevent bugs, reduce server crashes, and keep your routes clean and predictable.

Want To Automate This?

You can even enforce this pattern using ESLint rules like:

Final Thoughts

Many subtle bugs in Express apps stem from forgetting to return after sending a response. Understanding this pattern helps avoid double responses, wasted processing, and strange bugs — especially in production systems.

Happy coding and cleaner route handling!

--

--

Aditya Yadav
Aditya Yadav

Written by Aditya Yadav

Software Engineer who talks about tech concepts in web development https://www.linkedin.com/in/aditya-yadav-01/