fridays / next-routes

Universal dynamic routes for Next.js
MIT License
2.47k stars 230 forks source link

Using next-routes with prefix #30

Closed davidnguyen11 closed 5 years ago

davidnguyen11 commented 7 years ago

Hi I have an issue like below:

server.js

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

  router.get('/:area', (req, res) => {
    return app.render(req, res, '/index', req.query);
  });

  router.get('*', (req, res) => handle(wrappedRequest(req), res));

  server.use('/prefix', router);
  server.use('/prefix/static', express.static('static'));

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

what I want to do is using next-routes? It worked fine when not using prefix.

Is there any way to integrate prefix to next-route?

fridays commented 7 years ago

I think you can't globally prefix a Next.js app like it's /static or /_next folders.

But you could do this in routes.js:

routes.add('index', '/prefix/:area')

And in server.js:

const routes = require('./routes')
const handle = routes.getRequestHandler(app)

app.prepare().then(() => {
  const server = express();
  server.use('/prefix/static', express.static('static'));
  server.use(handle);

  server.listen(3000, err => {
    if (err)
      throw err;
    console.log(`> Ready on http://localhost:3000/prefix`);
  });
});
davidnguyen11 commented 7 years ago

Hi @fridays!

Thanks for quick replying.

My solution is just exactly like you & I added some changes to it.

nextjs: ver 2.1.1

routes.js

const nextRoutes = require('next-routes');
const routes = module.exports = nextRoutes();

routes.add('index', '/prefix/:area');

server.js

const routes = require('./routes')
const handle = routes.getRequestHandler(app)

app.prepare().then(() => {
  const server = express();

  // create router from express
  const router = express.Router();

  // this will prevent 404 not found when copy & parse url in browser's address bar
  router.get('/:area', (req, res) => {
    return app.render(req, res, '/index', req.query);
  });

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

  // bind prefix to resource url
  server.use('/prefix', router); 
  server.use('/prefix/static', express.static('static'));
  server.use(handle);

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

_document.js Because I use prefix so that I have to modified _document.js to override <NextScript /> component to serve resource with prefix like this: https://github.com/zeit/next.js/issues/257#issuecomment-296143438

AliasT commented 6 years ago

@davidnguyen179 @fridays it's working,but how about a route scope like express router

scope = routes.addScope("/prefix")
scope.add(somename, "/user")     // ===> "/prefix/user"
sean256 commented 6 years ago

I'm using Next 5.0 which supports "zones" and asset prefixes. I was running into an issue where next-routes was not working well with 5.0's prefixes.

In case someone else has this issue, here is my hack until something better comes along:

const routes = require('next-routes')();

routes
  .add('postsPrefix', '/prefix/posts/:id?', 'posts')
  .add('posts', '/posts/:id?', 'posts'); // both have to exist

module.exports = routes;

Then to go to a prefixed page supported by next-routes:

Router.pushRoute('postsPrefix', { id });
zvictor commented 6 years ago

I wanted to have all my routes prefixed with assetPrefix, but I didn't manage to export it from the config file in a clean way.

My solution was to replicate assetPrefix in the public settings and override Link with it:

next.config.js

const PREFIX = '/myAppPrefix'

module.exports = {
  assetPrefix: PREFIX,
  publicRuntimeConfig: {
    assetPrefix: PREFIX,
  },
}

routes.js

const { mapProps } = require('recompose')
const NextLink = require('next/link').default
const config = require('next/config').default()

const mapper = ({ as, href, ...props }) => {
  const { assetPrefix } = config.publicRuntimeConfig

  return {
    ...props,
    as: `${assetPrefix}${as}`,
    href: `${assetPrefix}${href}`,
  }
}

const Link = mapProps(mapper)(NextLink)
const routes = (module.exports = require('next-routes')({ Link }))

routes.add('home', '/', 'index')
...
stanleyfok commented 6 years ago

My solution is to modify the findAndGetUrls method. I am using the ENV var named basedPath. It works nicely

next.config.js

module.exports = {
  assetPrefix: envConfig.ASSET_PREFIX,

  publicRuntimeConfig: {
    basePath: envConfig.BASE_PATH,
  },
}

server.js

...
          const server = express();
          const router = express.Router();

          router.get('*', (req, res) => handle(req, res));

          server.use(envConfig.BASE_PATH, router);

          server.listen(envConfig.PORT, (err) => {
            if (err) throw err;

            console.log(`> Ready on http://localhost:${envConfig.PORT}`);
          });
...

routes.js

const escapeRegExp = require('lodash/escapeRegExp');
const config = require('next/config').default();

const routes = require('next-routes')();

const oldFindAndGetUrls = routes.findAndGetUrls.bind(routes);

routes.findAndGetUrls = (nameOrUrl, params) => {
  const { basePath } = config.publicRuntimeConfig;

  let mNameOrUrl = nameOrUrl;
  if (basePath && basePath !== '/') {
    const re = new RegExp(`^${escapeRegExp(basePath)}`);
    mNameOrUrl = nameOrUrl.replace(re, '');
  }

  const findAndGetUrls = oldFindAndGetUrls(mNameOrUrl, params);

  if (basePath && basePath !== '/') {
    findAndGetUrls.urls.as = `${basePath}${findAndGetUrls.urls.as}`;
  }

  return findAndGetUrls;
};

// usage: routes.add(name, pattern, page);
routes
  .add('index', '/', 'index')
  .add('login', '/login', 'login');

module.exports = routes;