Apal Shah
Apal Shah

Reputation: 690

Koa JS - Cannot Send Response after calling an API. Always Getting 404

Here is my code :

const koa = require('koa');
const app = new koa();
const https = require('https')

let email = "[email protected]"
let password = "mysecret"

var options = {
    host: 'myhost',
    port: 443,
    path: '/api/login?email=' + email + '&password=' + password,
    method: 'POST'
};

async function https_req(options, callback) {
    https.request(options, function(res) {
        res.setEncoding('utf8');
        res.on('data', function (chunk) {
            callback(chunk)        
        })
    }).end();
}

app.use(async (ctx, next) => {
    let res = await https_req(options, (result) => {
        console.log("final result: ",result)
        ctx.body = result
    })
})

app.listen(3000)

In this code, it gives proper result in console but in webpage it only shows Not Found

I have tried this with express and it was working perfectly fine but then I wanted to give a shot to Koa. It is working properly if I just type ctx.body = 'some data' without calling any function. I think koa is not waiting even if I write await.


I also tried async await inside callback function :

await https_req(options, async (result) => {
    console.log("final result: ",result)
    ctx.body = await result
})

But it always gives "Not Found".

I also want to know why this is happening. And what should I do to make it work.

Upvotes: 2

Views: 1456

Answers (1)

Rich Churcher
Rich Churcher

Reputation: 7654

So, this is an example of mixing callback-based code and promise-based code making things a bit tricky. What's going on is this: https_req returns a promise, but only because all functions declared async return promises. What it doesn't do is resolve when the result from the HTTPS request is available. awaiting it therefore does not do what you think it will! Execution immediately continues, and since there are no more statements and the response body is never set, a 404 is the result.

Instead, you should wait for the HTTPS response. The idea is to listen for the end event, adding up all the chunks until you receive it, and only then doing something with the result. You were calling your callback after the first chunk, which might work but is not guaranteed to do so.

When we put those things together we get something like this:

async function https_req (options) {
  return new Promise(resolve => {
    let result = ''

    http.request(options, res => {
      res.setEncoding('utf8')
      res.on('data', chunk => { result += chunk })
      res.on('end', resolve)
    }).end()
  })
}

Hopefully it's obvious that this is not a robust solution, does no error checking etc. At a bare minimum, you'd invoke it like this:

ctx.body = await https_req(options)

Upvotes: 2

Related Questions