Node.js服务端发起REST请求遇ETIMEDOUT及HTTP Code 0问题排查
Hey there! Let's break down what's going on here and fix that timeout issue.
First, let's diagnose the timeout problem
The ETIMEDOUT error means your Express server can't establish a connection to https://jsonplaceholder.typicode.com/posts. Here's why this might happen, even though your browser works:
- Network environment differences: Browsers often use system-wide proxy settings automatically, but the
xmlhttprequestpackage in Node.js doesn't. If your server is running in a restricted environment (like a corporate network with a proxy), it won't route outbound requests unless explicitly configured. - Limitations of the
xmlhttprequestpackage: This library is meant to mimic browser XHR behavior in Node, but it's not optimized for server-side use. Node has better built-in tools for HTTP requests.
First, test if your server can even reach the target URL: run curl https://jsonplaceholder.typicode.com/posts in the terminal where your Express server is running. If this times out too, you need to fix your network access (configure a proxy, adjust firewall rules, etc.). If curl works, the issue is with how the xmlhttprequest package is set up.
Second, fix your code logic (separate from the timeout)
You have a small but important bug: you're sending the response to your client (res.status(200).json(...)) immediately, before the outbound request to JSONPlaceholder finishes. This means your client gets a success message, but the server's request might still be in progress (or failing) in the background. You should only send the response once the outbound request completes.
Better alternatives to xmlhttprequest for Node.js
Instead of using a browser-like XHR library, use tools built for Node.js. Here are two solid options:
Option 1: Use Node's built-in https module
No extra dependencies needed:
const express = require('express'); const https = require('https'); const router = express.Router(); router.get('/', (req, res, next) => { const url = 'https://jsonplaceholder.typicode.com/posts'; https.get(url, (externalRes) => { let rawData = ''; // Collect response chunks externalRes.on('data', (chunk) => { rawData += chunk; }); // When response is complete externalRes.on('end', () => { if (externalRes.statusCode === 200) { console.log("It worked!"); const posts = JSON.parse(rawData); console.log(posts); res.status(200).json({ message: 'Handling GET requests to /product', fetchedPosts: posts }); } else { res.status(externalRes.statusCode).json({ error: 'Failed to fetch posts' }); } }); }).on('error', (err) => { console.error('Request error:', err); res.status(500).json({ error: err.message }); }); }); module.exports = router;
Option 2: Use node-fetch (Promise-based, cleaner syntax)
First install the package:
npm install node-fetch
Then update your code:
const express = require('express'); const fetch = require('node-fetch'); const router = express.Router(); router.get('/', async (req, res, next) => { try { const url = 'https://jsonplaceholder.typicode.com/posts'; const externalRes = await fetch(url); if (!externalRes.ok) { throw new Error(`Request failed with status: ${externalRes.status}`); } const posts = await externalRes.json(); console.log("It worked!"); console.log(posts); res.status(200).json({ message: 'Handling GET requests to /product', fetchedPosts: posts }); } catch (err) { console.error('Request error:', err); res.status(500).json({ error: err.message }); } }); module.exports = router;
To wrap up
- First verify your server can reach JSONPlaceholder via
curl— if not, fix your network/proxy settings. - Ditch the
xmlhttprequestpackage for Node-native tools or modern libraries likenode-fetch; they're more reliable for server-side requests. - Make sure to send your client's response only after the outbound request completes (success or failure).
内容的提问来源于stack exchange,提问作者Soxxes

