Next.js: Cannot reach API routes when using custom server

Created on 26 Jul 2019  路  10Comments  路  Source: vercel/next.js

Question about Next.js

I've set up a custom server.js, and also create an api as /pages/api/mock.ts.

The problem is, when I tried to fetch('api/mock'), I always got response with content OK instead of a JSON. But if I start Next app without custom server, api routes works as expected.

I cannot find examples for using API route with a custom server. Here's my configuration:

In server.js

const Koa = require('koa');
const Router = require('koa-router');
const next = require('next');

const dev = process.env.NODE_ENV !== 'production';
const port = process.env.PORT || 3000;
const app = next({ dev });
const handle = app.getRequestHandler();

app.prepare().then(() => {
    const server = new Koa();
    const router = new Router();

    server.use(router.routes());

    /* some routes except `/api/` */

    router.get('*', async (ctx, next) => {
        await handle(ctx.req, ctx.res);
    });

    server.listen(port, err => {
        if (err) {
            throw err;
        }
        console.log(`> WEB Ready on http://localhost:${port}`);
    });
});

/pages/api/mock.ts

export default (_: NextApiRequest, res: NextApiResponse) => {
    // this will be triggered but it send 'OK' back instead of a JSON string
    console.log('hit');
    const mock = JSON.stringify({data: 'foo' });
    res.setHeader('Content-Type', 'application/json; charset=UTF-8');
    res.statusCode = 200;
    res.end(mock);
};

Reproduction

Please clone this repo: https://github.com/jesseminn/next-bug-demo

Most helpful comment

Found the solution. I think it's because Koa has built-in response handling, and setting ctx.respond = false can bypass it. Check the official documentation about ctx.respond

app.prepare().then(() => {
    const server = new Koa();
    const router = new Router();

    server.use(router.routes());

    /* some routes except `/api/` */

    router.get('*', async (ctx, next) => {
        await handle(ctx.req, ctx.res);
        ctx.respond = false;
    });

    server.listen(port, err => {
        if (err) {
            throw err;
        }
        console.log(`> WEB Ready on http://localhost:${port}`);
    });
});

All 10 comments

I also was having same situation previously, i was having wrong directory name for the apis, it should be api.

Now your case, shouldn't it be pages???

I also was having same situation previously, i was having wrong directory name for the apis, it should be api.

Now your case, shouldn't it be pages???

Sorry I've corrected the typo. I did put it in pages/api/, and the controller is triggered but did not send back the JSON.

how do you run your app?? I hope you do build the next and then do the custom server run?

how do you run your app?? I hope you do build the next and then do the custom server run?

I starts the server with nodemon in dev & with node in production mode, both way have the same issue.

I just tested it and it works fine. Could you provide minimal reproduction where an error occurs?

maybe you have conflicting routes, e.g. with a dynamic first part /:somethingDynamic/ which gets called when you'd expect /api/ to be called. try handling the /api/ route first or disambiguate:

router.get('/api/:params?', async (ctx, next) => {
    await handle(ctx.req, ctx.res);
});

server.use(router.routes());    

I think the main issue here would be that req.url might be wrong because of the framework you're using, we've seen this happen before with express apps 馃

maybe you have conflicting routes, e.g. with a dynamic first part /:somethingDynamic/ which gets called when you'd expect /api/ to be called. try handling the /api/ route first or disambiguate:

router.get('/api/:params?', async (ctx, next) => {
  await handle(ctx.req, ctx.res);
});

server.use(router.routes());  

Hi, thanks for reply. I've tried adding the route but didn't fix the problem.

I've added a small example to reproduce the issue, please check https://github.com/jesseminn/next-bug-demo

Thanks!

i haven't used koa before, so i can't tell if there's a problem with it. the code looks reasonable to me, though. when i exchange koa for express in your repo, it works.

Found the solution. I think it's because Koa has built-in response handling, and setting ctx.respond = false can bypass it. Check the official documentation about ctx.respond

app.prepare().then(() => {
    const server = new Koa();
    const router = new Router();

    server.use(router.routes());

    /* some routes except `/api/` */

    router.get('*', async (ctx, next) => {
        await handle(ctx.req, ctx.res);
        ctx.respond = false;
    });

    server.listen(port, err => {
        if (err) {
            throw err;
        }
        console.log(`> WEB Ready on http://localhost:${port}`);
    });
});
Was this page helpful?
0 / 5 - 0 ratings

Related issues

rauchg picture rauchg  路  3Comments

swrdfish picture swrdfish  路  3Comments

knipferrc picture knipferrc  路  3Comments

ghost picture ghost  路  3Comments

renatorib picture renatorib  路  3Comments