reactjsisomorphic-javascriptreact-starter-kitisomorphic

Define Isomorphic React routes in Web API


I am trying to build a content management system with Isomorphic React. What I'm trying to determine is how I can define the routes with a RESTful request response object.

I am looking at the routes module in the React Starter Kit and I don't understand the logic of the route object because its using syntax I'm unfamiliar with. It looks as though the children are an array of promises and async executes each one. However const route = await next(); will produce all the data associated with the route. Is the there some route matching logic happening here?

The routes module is called from the server.js

const route = await UniversalRouter.resolve(routes, {
  path: req.path,
  query: req.query,
});

Here is the definition in the index.js which is in the parent directory of each route.

/* eslint-disable global-require */

// The top-level (parent) route
export default {

  path: '/',

  // Keep in mind, routes are evaluated in order
  children: [
    require('./home').default,
    require('./contact').default,
    require('./login').default,
    require('./register').default,
    require('./about').default,
    require('./privacy').default,
    require('./admin').default,

    // Wildcard routes, e.g. { path: '*', ... } (must go last)
    require('./notFound').default,
  ],

  async action({ next }) {
    // Execute each child route until one of them return the result
    const route = await next();

    // Provide default values for title, description etc.
    route.title = `${route.title || 'Untitled Page'} - www.reactstarterkit.com`;
    route.description = route.description || '';

    return route;
  },

};

The RESTful request I'm trying to incorporate works on my server.js or when I put it in the async action.

  const routeList = await fetch('/graphql', {
    method: 'post',
    headers: {
      Accept: 'application/json',
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      query: '{routes{path,page,chunk,data{articles}}}',
    }),
    credentials: 'include',
  });
  const routeData = await routeList.json();
  if (!routeData || !routeData.data || !routeData.data.routes) throw new Error('Failed to load the routes.');
  console.log(JSON.stringify(routeData, null, 2));

Solution

  • React starter kit implements Universal Router. All routes are just JS objects with 'path', 'action' and (optionally) 'children' attributes, so you can manipulate them as you please.

    The "magic" you are talking about is just a route's action used as middleware. So the data is actually fetched from whichever child route matches first (one of 'home', 'contact', 'login', etc).

    I'm not 100% sure the best way to implement the fetch-routing style you want but you could do it prior to injecting the routes. In src/client.js (around line 103) something like:

    Note: this is untested but it should give you a base to start

    const routeList = await fetch('/graphql', {
      method: 'post',
      headers: {
        Accept: 'application/json',
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        query: '{routes{path,page,chunk,data{articles}}}',
      }),
      credentials: 'include',
    });
    const routeData = await routeList.json();
    routeData.data.routes.forEach((route) => {
        routes.children.push({
           path: route.path,
           action() {
             return <Page>Render your page here</Page>;
           }
        })
    })
    
    const route = await UniversalRouter.resolve(routes, {
      ...context,
      path: location.pathname,
      query: queryString.parse(location.search),
    });