Inert

repository·master·Indexed 42 days ago

https://github.com/hapijs/inert

A plugin for hapi.js (version 7.1.2) that provides specialized handlers for serving static files and directories. It adds the h.file() method to the response toolkit and exposes 'file' and 'directory' handlers for route configurations, including support for ETag caching, pre-compressed files, and directory listings.

Tokens
2.4K
Snippets
9
Records
14
Agent score
44%

What's inside Inert

  1. What is @hapi/inert

    master
    @hapi/inert is a plugin for the hapi web framework that provides handlers for serving static files and directories. While it is designed to work seamlessly within the hapi ecosystem, it is a standalone module that can be used with other web frameworks.
  2. Install and register @hapi/inert

    master

    To use @hapi/inert for serving static files, you must first register it with your Hapi server. Once registered, it provides the h.file() method on the response toolkit and exposes 'file' and 'directory' handlers for your routes.

    const Hapi = require('@hapi/hapi');
    const Inert = require('@hapi/inert');
    
    const server = new Hapi.Server({
        port: 3000,
        routes: {
            files: {
                relativeTo: Path.join(__dirname, 'public')
            }
        }
    });
    
    const provision = async () => {
        await server.register(Inert);
        // ... setup routes
        await server.start();
    };
    
    provision();
  3. Configure @hapi/inert server options

    master

    You can configure @hapi/inert via the plugins.inert key in your Hapi server configuration.

    • etagsCacheMaxSize (number): Sets the maximum number of file etag hash values stored in the cache. Defaults to 1000.
  4. Register the @hapi/inert plugin

    master

    To use Inert in your hapi server, register it as a plugin. Inert does not support registration options; the options object passed to register must be empty. It requires hapi version >=20.0.0.

    await server.register({
        plugin: require('@hapi/inert'),
        options: {}
    });
  5. Handle file access errors

    master

    Inert uses Boom to signal file access errors:

    • Boom.notFound(): For missing or hidden files.
    • Boom.forbidden(): For files that exist but cannot be accessed.
    • Boom.internal() / 'system': For unexpected configuration or processing errors.

    Errors may contain an err.data.path property indicating the path that failed.

  6. Serve a single file with the file handler

    master

    The file handler is used in a route configuration to serve a specific file. The path can be a string (relative to the route's files.relativeTo config) or a function that returns the path.

    server.route({
        method: 'GET',
        path: '/{path*}',
        handler: {
            file: 'page.html'
        }
    });
  7. Create a static file server with the directory handler

    master

    The directory handler allows you to serve an entire folder. Routes using this handler must include a path parameter at the end of the path string (e.g., /{param*}).

    Common options for the directory handler:

    • path: The directory root (string, array of strings, or a function returning them).
    • index: If true, looks for index.html. Can be a specific string or array of strings.
    • redirectToSlash: Redirects requests for a directory without a trailing slash to the version with a slash.
    • listing: Enables directory listing if no index file is found.
    server.route({
        method: 'GET',
        path: '/{param*}',
        handler: {
            directory: {
                path: '.',
                redirectToSlash: true,
                index: true,
            }
        }
    });
  8. Configure h.file(path, [options])

    master

    The h.file() method transmits a file from the file system. The Content-Type header is automatically determined by the filename extension.

    Options:

    • confine (boolean): If true, returns 403 Forbidden if the path resolves outside the confine directory. Defaults to true (uses route's relativeTo).
    • filename (string): Overrides the Content-Disposition filename.
    • mode (string): Controls Content-Disposition. Values: 'false' (default), 'attachment', or 'inline'.
    • lookupCompressed (boolean): If true, looks for pre-compressed versions (e.g., .gz). Defaults to false.
    • lookupMap (object): Maps encoding to extensions. Default: { gzip: '.gz' }.
    • etagMethod (string|false): 'hash' (default, SHA1), 'simple' (size/date), or false to disable.
    • start (number): Byte offset to start reading. Defaults to 0.
    • end (number): Byte offset to stop reading.
  9. Use h.file() for customized file responses

    master

    Inside a route handler, you can use h.file(path, [options]) to respond with a file. This is useful for dynamic file selection based on request logic or error handling.

    server.route({
        method: 'GET',
        path: '/file',
        handler(request, h) {
            let path = 'plain.txt';
            if (request.headers['x-magic'] === 'sekret') {
                path = 'awesome.png';
            }
    
            return h.file(path).vary('x-magic');
        }
    });
    
    // Example: Custom 404 page
    server.ext('onPreResponse', (request, h) => {
        const response = request.response;
        if (response.isBoom && response.output.statusCode === 404) {
            return h.file('404.html').code(404);
        }
        return h.continue;
    });
  10. Configure the directory handler

    master

    The directory handler generates an endpoint for serving content from a directory. It is an object with the following options:

    • path (required): The directory root. Can be a string, an array of strings, or a function function(request).
    • index (boolean|string|string[]): Determines if an index file is served. true looks for index.html. Defaults to true.
    • listing (boolean): If true, generates a directory listing when no index is found. Defaults to false.
    • showHidden (boolean): If true, shows hidden files. Defaults to false.
    • redirectToSlash (boolean): Redirects requests without a trailing slash. Defaults to false.
    • lookupCompressed (boolean): Looks for pre-compressed versions. Defaults to false.
    • lookupMap (object): Maps encoding to extensions. Default: { gzip: '.gz' }.
    • etagMethod (string|false): 'hash' (default), 'simple', or false.
    • defaultExtension (string): Appended to requests if the file is not found.
  11. Configure the Inert etagsCacheMaxSize setting

    master

    You can configure the maximum size of the ETag cache via the server's plugin settings. This is done by providing an inert object within your server configuration. The etagsCacheMaxSize option must be a non-negative integer (defaults to 1000 if not specified).

    const server = Hapi.server({
        // ... other server settings
        plugins: {
            inert: {
                etagsCacheMaxSize: 500
            }
        }
    });
  12. Use the file toolkit method

    master

    Inert decorates the hapi toolkit (the h object) with a file method. This is useful when you want to return a file response from within a handler using the toolkit.

    Key options for h.file(path, responseOptions):

    • confine: A string defining the directory boundary. Defaults to '.' if undefined or true. This prevents access to files outside the specified directory.
    • start: The byte offset to start reading from.
    • end: The byte offset to stop reading at.

    Note: options.start must be less than or equal to options.end.

    server.route({
        method: 'GET',
        path: '/download',
        handler: (request, h) => {
            return h.file('/path/to/file.zip', {
                start: 0,
                end: 1024
            });
        }
    });