0

I have a NextJS app and am using next-routes to handle all routing.

My routing module currently looks like this:

const routes = require('next-routes')();
const { getEntries } = require('../data/contentful');

module.exports = async () => {
  const globalSettings = await getEntries({
    content_type: 'globalSettings',
  });

  routes
    .add('caseStudies', `/${globalSettings.fields.caseStudiesSlug}`, 'caseStudies')
    .add('caseStudy', `/${globalSettings.fields.caseStudiesSlug]}/:slug`, 'caseStudy')
    .add('home', `/`, 'index')
    .add('page', `/:slug*`, 'page'));

  return routes;
};

I can get this working for server side, but to use next-routes on client side, I need this module to immediately return the routes object rather than an async function. e.g.

const routes = require('next-routes')();
const { getEntries } = require('../data/contentful');

// Do this first, then module.exports
const globalSettings = await getEntries({
  content_type: 'globalSettings',
});

module.exports = routes
  .add('caseStudies', `/${globalSettings.fields.caseStudiesSlug}`, 'caseStudies')
  .add('caseStudy', `/${globalSettings.fields.caseStudiesSlug]}/:slug`, 'caseStudy')
  .add('home', `/`, 'index')
  .add('page', `/:slug*`, 'page'));

This doesn't work because await must be inside an async function. How can I complete my async API call before doing my module.exports of the routes object?

CaribouCode
  • 13,998
  • 28
  • 102
  • 174
  • I'm not sure you can, since `require` is synchronous, it can't wait for an API call to end to import your module. BTW, let's say your server answers in dozen of seconds, what do you expect it to do ? If it could wait for your api call to end, it would probably froze your server until it's done. – Serge K. Feb 19 '19 at 17:32
  • Why again does it need to be synchronous? – Kevin B Feb 19 '19 at 17:45
  • @KevinB Because the next-routes object can be imported on client side and destructured like so `import { Link } from '../routes';` – CaribouCode Feb 20 '19 at 09:03
  • wait... so the only reason you want is synchronous is so you can destructure it? that’s a bit silly imo. – Kevin B Feb 20 '19 at 15:49
  • @KevinB This is how the next-routes package works. It's a popular addition to NextJS and not something I have control over. – CaribouCode Feb 20 '19 at 17:35
  • I meant it's silly as destructing isn't a necessary thing for you to do there. There must be some other reason. – Kevin B Feb 20 '19 at 17:36

1 Answers1

0

This is a special case of this renowned problem. Synchronous code can be transformed to asynchronous but not vice versa.

As shown in this similar answer, promises should be used up to application entry point if needed:

module.exports = (async () => {
  const globalSettings = await getEntries({
    content_type: 'globalSettings',
  });

  return routes
  .add('caseStudies', `/${globalSettings.fields.caseStudiesSlug}`, 'caseStudies')
  .add('caseStudy', `/${globalSettings.fields.caseStudiesSlug]}/:slug`, 'caseStudy')
  .add('home', `/`, 'index')
  .add('page', `/:slug*`, 'page'));
})();

This module also exports a promise so it needs to be chained in a module that depends on it.

There is a proposal for top-level await that is intended to provide syntactic sugar for this recipe.

Estus Flask
  • 206,104
  • 70
  • 425
  • 565
  • Top level await is exactly what I need! – CaribouCode Feb 20 '19 at 09:00
  • Sadly, it isn't even near to being approved and implemented natively. Exporting async IIFEs like the answer shows and importing them with `await require('async-module')` is what we have now. You can see that the proposal actually describes this, https://github.com/tc39/proposal-top-level-await#workaround-export-a-promise-to-represent-initialization – Estus Flask Feb 20 '19 at 09:08
  • I'm struggling to work out a solution to my problem then. Basically on server side I need to do my globalSettings API call and build the routes object. Then on client side, I want to be able to access that routes object without doing the API call again. – CaribouCode Feb 20 '19 at 09:12
  • I don't use Next myself. If you have problems accessing an object from server side on client side then that's more specific problem. Basically this is done by rendering index page with `` but there could be solutions that are specific to Next. – Estus Flask Feb 20 '19 at 09:19