javascriptnode.jsexpress

Express calling post for application/json but request.write(body) generates a "write after end" error


My code is a first node process (login) calling a second node process (api)

Every thing is fine until I call the http.get request (for a POST) The error occurs when it calls the

request.write(body)

I get the following error "Error [ERR_STREAM_WRITE_AFTER_END]: write after end"

Here is the code of the caller :

app.enable('strict routing');
app.disable('x-powered-by');

app.use(express.json());
app.use(express.urlencoded({
    extended:true
}));
 
assert(hbs);
app.set('view engine','hbs');
app.engine('hbs',hbs.create().__express)

svr.server = http.createServer(app)

app.get('/',function(req,res,next) {
    res.render("login.hbs")
})
app.post('/login', async function(req,res) {
    console.log("post login",req.body)

    const password = req.body.password
    console.log('password',password)

    const body = JSON.stringify({
        appid:appid,
        authtoken:authtoken,
        password:password        
    })

    const options = {
        host: api.host,
        port: api.port,
        path: '/api/login',
        method: "POST",
        headers: {
            'Content-Type':'application/json',
            'Content-Length':Buffer.byteLength(body)
        },
    }

    console.log('options',options)

//    var data = ''
    var request = http.get(options, function(res) {
        let data = ''
        res.setEncoding('utf8')
        res.on('data',function(chunk) {
            data += chunk
        })
        res.on('end',function() {
            console.log('data',JSON.parse(data))
        })
        res.on('error',function(e) {
            console.log('err',e.toString())
        })
    })
    request.write(body)
    request.end()

    res.end()
})

app.listen(svr.port,svr.host,function() {
    console.log('http listen',svr.port,svr.host)
})

On the other side the called party :

app.enable('strict routing');
app.disable('x-powered-by');

app.use(express.json())
app.use(express.urlencoded({
    extended:true
}))

svr.server = http.createServer(app)

app.get('*', async function(req,res,next) {
    console.log('all')
    next()
}) 
app.post('/api/login', async function(req,res) {
    console.log("post api login",req.body)

    const account = ''
    res.status(200).json({ account:account, session:uuid.v4() })
    // res.status(400).json({ error:'server error message' })
})

app.listen(svr.port,svr.host,function() {
    console.log('http listen',svr.port,svr.host)
})

Solution

  • From the docs for http.get()

    The only difference between this method and http.request() is that it sets the method to GET by default and calls req.end() automatically.

    You cannot call request.write() since the write buffer has already been closed. Why are you using http.get() to attempt a POST request anyway?

    Node.js supports the modern Fetch API since v18.0.0. I would strongly suggest you use it instead of the http module

    app.post('/login', async (req, res, next) => {
      const { password } = req.body;
      try {
        const response = await fetch(`http://${api.host}:${api.port}/api/login`, {
          method: 'POST',
          body: JSON.stringify({ appid, authtoken, password }),
          headers: { 'content-type': 'application/json' },
        });
    
        if (!response.ok) {
          console.error('Request failed', response.status, await response.text());
          throw new Error(`Upstream request failed: ${response.status}`);
        }
    
        const data = await response.json();
        console.log('data', data); // you sure that's all you want to do with data?
    
        res.end();
      } catch (e) {
        next(e);
      }
    });