Search code examples
service-workercreate-react-app

create-react-app: Using the network for certain service worker navigations?


I am using the create-react-app template. I have the service worker installed at the root directory. Therefore, any clicked link(a tag not Link tag) to my domain will call the service worker. The service worker will then return the resource from the cache if available otherwise it will make a call to the network. This is correct right?

Assuming this is correct, I now have a slightly different scenario, I want to add a /documentation route to my app. This route will server the index.html file create by jsdocs using node js(see route I have in node js code).

The problem is that it appear the service worker takes this route, never calls the node js backend, and sends this to my react router. The react router, since it has no /documentation route, just shows the navigation and footer component attached to all / routes.

I have a two question:

1. How do I specify that a certain route should not be handled my the service worker? I think I can use fetch but I am unsure how to implement it correctly

2. Why does the service worker not see that it does not have a /documentation route saved and therefore just call the index.html files from the server?

Node js

const path = require('path');
const bodyParser = require('body-parser');
const fs = require('fs');
const MongoClient = require('mongodb').MongoClient;
const stringToObject = require('mongodb').ObjectID
const mongoStoreFactory = require("connect-mongo");
const compression = require('compression');
const helmet = require('helmet');


var app = express();

app.use(helmet());  
//Compress here since we do not want to change the build tools. 
//This will use a bit more CPU as it needs to compress each request and response.
app.use(compression())
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.set("port", process.env.PORT || 3001);
console.log("Port: " + process.env.PORT + " mode:  " + process.env.NODE_ENV);
app.use(express.static("client/build"));
app.use(express.static("client/out"));


var accountsCollection = null; 
/**
    We don't need to specify index since this will be served automatically with static files. 
*/
MongoClient.connect("mongodb://db:27017", function(err, db) {
  if(!err) {
    console.log("We are connected");
    db.collection('accounts', function(err, collection) {
        if(!err){
            console.log("Accessed account collection");
            accountsCollection = collection

        }
    });
    //app.get('/', function (req, res) {
    //    console.log("Get index!");
    //    res.sendFile(path.join(__dirname+'/client/build/index.html'));
    //});
    app.get('/about', function (req, res) {
        console.log("Get about!");
        res.sendFile(path.join(__dirname+'/client/build/about.html'));
    });
    app.get('/services', function (req, res) {
        console.log("Get services!");
        res.sendFile(path.join(__dirname+'/client/build/services.html'));
    });
    app.get('/work', function (req, res) {
        res.sendFile(path.join(__dirname+'/client/build/work.html'));
    });
    app.get('/skills', function (req, res) {
        res.sendFile(path.join(__dirname+'/client/build/skills.html'));
    });

    app.get('/documentation', function (req, res) {
        console.log("Get docs!");
        res.sendFile(path.join(__dirname+'/client/out/index.html'));
    });

    app.listen(app.get("port"), () => {});

  }
});

Call to documentation route in react component skills

        <article className={"skills__skill"}>
          <a href={"/documentation"}> See Documentation </a>
        </article> 

My complex router

<div className={"app"}>
    <Router>
      <div>
        <Route render={({location, history, match}) => {
        return (
              <RouteTransition 
                pathname={location.pathname}
                atEnter={{ opacity: 0 }}
                atLeave={{ opacity: 0 }}
                atActive={{ opacity: 1 }}
              >
          <Switch key={location.key} location={location}>
          <Route exact path={"/"} render={()=>{
            handlePageChange(history);
            return <Start/>
            }
          }/>

          <Route path={"/"} 
            render={({location, history, match})=>{
                return (
                  <RouteTransition 
                    pathname={location.pathname}
                    atEnter={{ opacity: 0}}
                    atLeave={{ opacity: 0}}
                    atActive={{ opacity: 1}}
                  >
                    <FadeBackground >
                        <Clouds>
                        <Switch key={location.key} location={location}>
                            <Route exact path={"/services"} 
                                render={(props)=>{
                                    handleAuthentication(props);
                                    handlePageChange(history);
                                    return <Home />
                                }
                            }/>
                            <Route exact path={"/about"} component={Profile}/>
                            <Route exact path={"/work"} 
                                render={()=>{
                                    handlePageChange(history);
                                    return <Work />
                                }}
                            />

                            <Route exact path={"/skills"} 
                                render={()=>{
                                    handlePageChange(history);
                                    return (
                                        <Skills />
                                    )
                                }}
                            />
                         </Switch>
                        </Clouds>
                    </FadeBackground>
                </RouteTransition>
                )
          }}/>
          </Switch>
        </RouteTransition>
            )}
        }/>
          <Nav
            links={[
                {name:"Welcome",location:"/"},
                {name:"About Me",location:"/about"},
                {name:"My Services",location:"/services"},
                {name:"My Work",location:"/work"},
                {name:"My Skills",location:"/skills"}
            ]}
            />
            <footer className={"footer"}>

            </footer>
      </div>
    </Router>
</div>

Solution

  • Under the hood, create-react-app uses sw-precache, run via sw-precache-webpack-plugin.

    In order to customize the behavior of the service worker, like with other customizations in create-react-app, you need to first eject.

    Once you have access to the underlying configuration options, the property you want to configure is navigateFallbackWhitelist, inside of webpack.config.prod.js. You can adjust the default configuration to include a different regular expression; any navigations that match one of the RegExps will trigger the service worker to respond, so the idea would be that you can set a RegExp that will match the paths that should be handled via your SPA HTML, and not match documentation/ or anything else that should be handled via your backend.