singlePageApp function

Handler singlePageApp(
  1. String file
)

Serves one file for every document request, for a single-page application.

A client-side router owns the path, so the server answers the same document whatever the browser asks for. Register it as a fallback, after the routes that should still win:

final app = Router()
  ..mount('/api', apiRoutes)
  ..fallback(singlePageApp('web/index.html'));

This answers one file and nothing else. For a whole build directory, which is the usual case, use staticFiles(directory, html: true) instead — it serves the assets beside the document and gets the cache policy right.

Only GET and HEAD are answered. A POST to a path nothing matched is a wrong request, not a page view, and handing it an HTML document would hide that.

Implementation

Handler singlePageApp(String file) {
  final url = file.split('/').last;
  final inner = shelf_static.createFileHandler(file, url: url);

  return (Request request) {
    if (request.method != 'GET' && request.method != 'HEAD') {
      return Response.notFound('no route for /${request.url.path}');
    }

    // `createFileHandler` only answers at the file's own URL, so the request is
    // pointed at it; the client-side route stays in the browser's address bar.
    return inner(
      Request(
        request.method,
        request.requestedUri.replace(path: '/$url'),
        headers: request.headers,
        context: request.context,
      ),
    );
  };
}