How to serve the same static files regardless of the route?

2k Views Asked by At

I have a react App. With webpack I build for production. Now I try to set a little koa server to serve the static files generated by webpack for production.

So I did this

import Koa from 'koa'
import serve from 'koa-static'

const app = new Koa()

app.use(serve('dist/'))

app.listen(3001)

Where dist/ is the directory where are the files (index.html, bundle etc). This works well but only for the route '/' (localhost:3001/) In my app I use react router so I need to go to /login (localhost:3001/login) by example. But when I try I get "Not Found". With the devServer (by webpack) this route works well. I just need to always serve /dist, whatever the route. How to do this whit koa ?

4

There are 4 best solutions below

0
On BEST ANSWER

Ok I finally won

import Koa from 'koa'
import serve from 'koa-static'
import fs from 'fs'
import path from 'path'

const app = new Koa()
const dist = path.join(__dirname, 'dist')
let validRoutes

fs.readdir(dist, (err, files) => {
  if (err) return console.log('Unable to scan directory: ' + err)
  validRoutes = files
})

function isRouteValid (url) {
  if (!validRoutes || !url) return false
  return validRoutes.find(route => url.slice(1) === route)
}

app.use(async (ctx, next) => {
  if (!isRouteValid(ctx.url)) ctx.path = 'index.html'
  await next()
})

app.use(serve(dist))

app.listen(3010)
2
On

One option is to intercept the react-router client routes in Koa and rewrite them to '/' to load index.html and the client assets.

const REACT_ROUTER_PATHS = [
  '/login',
  '/logout',
  '/other-client-path'
];

app
  .use(async (ctx, next) => {
    if (REACT_ROUTER_PATHS.includes(ctx.request.path)) {
      ctx.request.path = '/';
    }
    await next();
  })
  .use(serve('dist/'));
2
On

If koa is like express-static it is normal that he returns 'Not Found' because the only file present is 'index.html'. My solution

import fs from 'fs';


app.use(serve('dist/'));
// If the file is not found by koa
// intercept all request and return index.html
// this way you can manage the request in React 
app.use((ctx, next) => {
    ctx.type = 'html';
    ctx.body = fs.readFileSync('./dist/index.html');
});
0
On

Depending on how your build is generated, just looking at the files in that generated dist directory might not be enough. If you're using CRA for example, you'll probably need something more like what they use in this post to find all valid routes in children directories too. So you might have something that looks like

import Koa from 'koa';
import serve from 'koa-static';
import fs from 'fs';
import path from 'path';

const app = new Koa();
const dist = path.join(__dirname, 'dist');
let validRoutes;

var walk = function(dir, done) {
    var results = [];
    fs.readdir(dir, function(err, list) {
        if (err) return done(err);
        var i = 0;
        (function next() {
            var file = list[i++];
            if (!file) return done(null, results);
            file = path.resolve(dir, file);
            fs.stat(file, function(err, stat) {
                if (stat && stat.isDirectory()) {
                    walk(file, function(err, res) {
                        results = results.concat(res);
                        next();
                    });
                } else {
                    results.push(file);
                    next();
                }
           });
        })();
    });
};

walk(dist, function(err, results) {
    if (err) throw err;
    validRoutes = results;
});

function isRouteValid (r: string) {
    if (!validRoutes || !r) return false;
    return validRoutes.find(route => r === route);
}

app.use(async (ctx, next) => {
    if (!isRouteValid(path.resolve(dist, ctx.url.slice(1)))) ctx.path = 'index.html';
    await next();
});

app.use(serve(dist));

app.listen(3010);