express-autoindex produce a directory listing like Nginx, Apache or another, but just with express
It takes into consideration most of the mime-types correctly, and page generation is fully customizable.
The objectives are:
- Make a
HTMLpage orJSONdata easily usable, on the great majority of browsers - Correctly take into consideration the majority of
MIME types, and the generation of the most customizable pages possible - Supports most common file encodings
- Native support for
Typescript,EcmaScriptandCommonJS - The least amount of dependency possible (currently only
two) - The
lightestpossible
- Install
- API
- Customization of the html page appearance
- Customization of the date format
- Customization of the JSON format
- Error handling
- Minimalist example
- Production mode
- Contributors
- To do list
- License
- Dependencies
# npm
npm install express-autoindex
# yarn
yarn add express-autoindeximport autoindex from 'express-autoindex';
// Root of server, ./public dir
app.use(autoindex('public'))
// Specific path `/files`, ./public dir
app.use('/files', autoindex('public'));
// Set options
app.use('/files', autoindex('public', { dirAtTop: false, displaySize: false }));Returns middlware that serves an index of the directory in the given path.
The path is based of the req.url value, so a req.url of '/some/dir
with a path of 'public' will look at 'public/some/dir'.
express-autoindex accepts options:
-
type:
booleanThrow error for all HTTP error codes (4xx & 5xx).
By default, errors will be generated only on 5xx types. If you wish to generate an error regardless of the HTTP error code, pass
trueto the option.Default to
false -
type:
number | falseCaches for a defined time the generated pages. Very useful to save server resources.
Pass
falseto disable the cache, or the number of milliseconds representing the cache expiration time.Default to
300000= 5 mins -
type:
object{ isDir: string, name: string, path: string, time: string, size: string }
By default, the json generated for a file or folder follows a precise structure. It is possible to rename or remove the key of this object.
More detail here
Default to
undefined -
type:
stringPass the relative path of your custom template file. For example, if the file is located in the same folder of your startup server file, simply write
my-file.htmlor./my-file.html.More detail here
Default to
undefined -
type:
stringCustom date print format.
More detail here
Default to
undefined -
type:
booleanDisplay directories before files
Default to
true -
type:
booleanDisplay the last modification date of the file or directory if available.
Default to
true -
type:
booleanDisplay dotfiles (.env, .yarnrc, ...).
Default to
false -
type:
booleanDisplay size of the file or directory if available.
Default to
true -
type:
RegExpRegular expression for files/dirs exclude, for example
/my-file.json|\*.cpp/. -
type:
booleanSend data in json format instead of an html page. Might be useful if you want to use the data for another application.
Default to
false -
type:
booleanAllow only
HEADandGETHTTP methods.Default to
true
It is possible to customize the entire HTML page sent to the client. To do this, write an HTML page as usual. Then simply pass the path to your file to the customTemplate option.
express-autoindex generates two variables:
- title : the title of the generated page. Since this is an autoindex, the title represents the folder path
- content : the contents of an html table representing the contents of the folder
To use variables in your template, simply call them between two curly brackets, like {{title}}.
Elements inside a row of table all have their own css class linked to it, for easy access via a css selector. These classes are all placed on a :
- link : url pointing to a file or folder
- size : folder or file size
- time : date of last folder or file modification
Don't forget that by default, every browser provides its own native css. If you want to standardize the look and feel, a little css will be necessary.
The html code below is the one generated by default. You can use it as inspiration to generate your own template.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>{{title}}</title>
</head>
<body>
<h1>{{title}}</h1>
<hr/>
<table>{{content}}</table>
<hr/>
</body>
<style type="text/css">
html {
font-family: Arial, Helvetica, sans-serif
}
table {
font-family: 'Courier New', Courier, monospace;
font-size: 12px;
font-weight: 400;
letter-spacing: normal;
line-height: normal;
font-style: normal;
}
tr td:first-child {
min-width: 20%;
}
td a {
margin-right: 1em;
}
td.size {
text-align: end;
}
</style>
</html>It is possible to customize the entire date format of file or folder. To do this, pass a string containing the new format to the dateFormat option.
The date is a string containing the desired keys, by default %d?-%mo-%y %h:%mi.
It is formatted in UTC format, and has eight keys:
- %wd → week day
- %d → day
- %mo → month
- %y → year
- %h → hours
- %mi → minutes
- %s → seconds
- %ms → milliseconds
It's possible for one of these keys to return an empty string if nothing is available.
Now let's imagine that a separator is just after it. If it appears when the key is empty, it's not aesthetically pleasing.
You can therefore add the ? character just after a key. This super character will ensure that the one following it only appears if the key is not empty.
For example, with the following format %d?-%y, if %d is empty, the string returned will be 2023 and not -2023.
US: %mo-%d?-$y %h:%mi
EU: %d?-%mo-%y %h:%mi
ISO: %y-%mo-%dT%h:%mi:%sZ
Custom format: <%d?-%y ~ %h>
It is possible to customize the entire JSON format of file or folder. To do this, pass an object containing the new key names to the customJsonFormat option.
By default, the object generated for an element is:
- isDir → (boolean) is a folder
- name → (string) the name
- path → (string) the path (url)
- time → (string) the UTC date of the last modification
- size → (number) file size,
nullon a folder
The option object works according to three simple rules:
- To change the name of the key, simply write an entry with the key you wish to change and the value representing its new name.
- If a key is missing, it will not be added.
- Any key that does not exist by default will not be added.
Here are a few examples to help you understand how it works:
| Options | Generated JSON |
{
"isDir": "isADirectory",
"name": "nameOfElement",
"path": "url",
"time": "date",
"size": "weight"
} |
{
"isADirectory": false,
"nameOfElement": "history.md",
"url": "/public/random_dir/history.md",
"date": "07-Jul-2022 08:19",
"weight": 348
} |
{
"name": "nameOfElement",
"path": "path"
} |
{
"nameOfElement": "history.md",
"path": "/public/random_dir/history.md"
} |
{
"name": "name",
"nonexistentKey": "defaultValue"
} |
{
"name": "history.md"
} |
express-autoindex will do its best to handle Node.js errors correctly by converting them into a valid HTTP error. The default error type is 500.
In no case express-autoindex handles custom error pages. The only thing done is to modify the statusCode of the res object and generate an error if necessary.
Below is a list of currently supported errors.
This is how to read the list: The Node error code → (the related HTTP code) "The error message".
- EACCES → (500) Permission denied
- EADDRINUSE → (500) Address already in use
- EBADF → (500) fd is not a valid open file descriptor
- ECONNREFUSED → (500) Connection refused
- ECONNRESET → (500) Connection reset by peer
- EEXIST → (500) File exists
- EFAULT → (500) Bad address
- EINVAL → (500) Invalid flag specified in flag
- EISDIR → (500) Is a directory
- ELOOP → (500) Too many symbolic links encountered while traversing the path
- EMFILE → (500) Too many open files in system
- ENAMETOOLONG → (414) URI Too Long
- ENOENT → (404) No such file or directory
- ENOMEM → (500) Out of memory
- ENOTDIR → (404) Not a directory
- ENOTEMPTY → (500) Directory not empty
- ENOTFOUND → (500) DNS lookup failed
- EOVERFLOW → (500) pathname or fd refers to a file whose size, inode number, or number of blocks cannot be represented in, respectively, the types off_t, ino_t, or blkcnt_t
- EPERM → (403) Operation not permitted
- EPIPE → (500) Broken pipe
- ETIMEDOUT → (408) Request Timeout
To handle these errors, all you need to do after calling this middleware is to use a code of this type:
import { STATUS_CODES } from 'http';
[...]
app.use((_req, res) => {
// 4xx errors
res.send(`<h1>${res.statusCode} ${STATUS_CODES[res.statusCode]}</h1>`);
});
app.use((err, _req, res, next) => {
// 5xx errors
res.send(`<h1>${res.statusCode} ${STATUS_CODES[res.statusCode]}</h1>`);
if (err)
console.error(err);
next();
});
[...]import express from 'express';
import autoindex from 'express-autoindex';
import type { Application, NextFunction, Request, Response } from 'express';
const app: Application = express();
const PORT = process.env.PORT || 3000;
app.disable('x-powered-by');
app.use(express.urlencoded({ extended: true }));
app.use(express.json());
app.use('/public', autoindex('public'));
app.listen(PORT, (): void => console.log(`server is running at ${PORT}`));When the variable process.env.NODE_ENV is set to production, error messages are much less detailed for security reasons.
Thanks to you and your help, express-autoindex is getting better every day. I would like to thank those people who gave their time 🧡
- Open the middleware for use outside express.js