Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dev server does not serve routes expected to be built by static export #2823

Closed
1 task done
tannerlinsley opened this issue Aug 21, 2017 · 7 comments
Closed
1 task done

Comments

@tannerlinsley
Copy link

  • I have searched the issues of this repository and believe that this is not a duplicate.

Expected Behavior

In dev mode, navigating to /blog/posts/my-post-title should be picked up by the router as a destination that will eventually be available on static export.

Current Behavior

In dev mode, navigating to /blog/posts/my-post-title is not be picked up by the router, and you get a 404 where a static HTML file would normally be located after exporting the static site.

Steps to Reproduce (for bugs)

  1. Use the following next.config.js:
// next.config.js
module.exports = {
  exportPathMap: function () {
    return {
      "/": { page: "/" },
      "/p/hello-nextjs": { page: "/post", query: { title: "hello-nextjs" } },
    }
  },
}
  1. Run the dev server with next
  2. Navigate to localhost:3000/p/hello-nextjs
  3. The result will be a 404

Context

The expected behavior is extremely relevant to using Next to build static websites. One key component to developing a static website is the ability for the local development server to resemble as closely as possible the conditions where the static site will be served. In this case, I would expect that the paths exported from exportPathMap would be used to serve the correct page.

You may be thinking that next-routes + <Link as='...' /> is a good solution to this problem, where the browser push state masks navigation to /blog/posts/my-post-title with something like blog-post?slug=my-post-title, but this only works when navigating from a root page or index, not when reloading or opening a new window.

@tannerlinsley
Copy link
Author

I'm not familiar enough (yet) with the source, but if it's any help to someone who is, my guess is that this could be easily fixed by adding the result of exportPathMap to the the built-in routing paths. Roughly around here, we could detect dev mode and serve the correct page template specified in exportPathMap for any routes it matches.

@ldthorne
Copy link
Contributor

Hey @tannerlinsley, what I did on my team is extract the routes into its own module that just returns an object with the route as the key and data about the route (e.g., page and query) as the value. We then consume this module in next.config.js by returning the object returned by the module and in our development server, where we route all get requests through middleware that looks for a route with the req's name and returns that page if found. Happy to provide some code examples if you'd like.

@tannerlinsley
Copy link
Author

Yeah, we've tried almost that exact same thing, though we don't like relying on a custom server, given we are only using next to export static sites. Would love to see your code regardless :)

@ldthorne
Copy link
Contributor

ldthorne commented Aug 23, 2017

Hey @tannerlinsley, sorry for the delay. We have three files:

routes.js:

module.exports = () => {
  return {
    '/': { page: '/' },
    '/404': { page: '/404' },
    '/about': { page: '/about' },
    ...
  }
}

developmentServer.js:

const express = require('express');
const next = require('next');
const { parse } = require('url');

const DEV = process.env.ENVIRONMENT !== 'production';
const PORT = 4567;

const app = next({dir: '.', dev: DEV});
const handle = app.getRequestHandler();

const getRoutes = require('lib/routes');

const routes = getRoutes();
app.prepare().then(() => {
  const server = express();
  server.get('*', (req, res) => {
    const parsedUrl = parse(req.url, true);
    const { pathname, query } = parsedUrl;
    const route = routes[pathname];
    if (route) {
      return app.render(req, res, route.page, route.query);
    }
    return handle(req, res);
  });

  server.listen(PORT, (err) => {
    if (err) throw err;
    console.log(`> READY FOR LIFOTFF http://localhost:${PORT}`);
  });
});

and finally, next.config.js:

const getRoutes = require('lib/routes');

module.exports = {
  exportPathMap: getRoutes
};

Let me know if you have any questions (or suggestions!).

@tannerlinsley
Copy link
Author

This works perfectly. Thanks guys.

@timneutkens
Copy link
Member

@ldthorne I guess it'd be good to document this in the wiki 👌

@majdi
Copy link

majdi commented Mar 20, 2018

Thanks for this solution @ldthorne. I just have a little issue about it;

I have a dynamic link like /episode?id=s01e01&title=pilot as /e/s01e01-pilot this works very well on dev server but I have an error when I serve /episode?id=s01e01&title=pilot around id query: Cannot read property 'id' of undefined

Is it a way to redirect to a 404 without having theses errors?

Thanks and cool for adding this solution on the wiki 🤓

@lock lock bot locked as resolved and limited conversation to collaborators Mar 20, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants