Giter VIP home page Giter VIP logo

fastify-static's Introduction

@fastify/static

CI NPM version js-standard-style

Plugin for serving static files as fast as possible. Supports Fastify version 4.x.

Fastify version branch
^4.x This branch
^3.x v5.x
^2.x 2.x
^1.11.x 1.x

Install

npm i @fastify/static

Usage

const fastify = require('fastify')({logger: true})
const path = require('node:path')

fastify.register(require('@fastify/static'), {
  root: path.join(__dirname, 'public'),
  prefix: '/public/', // optional: default '/'
  constraints: { host: 'example.com' } // optional: default {}
})

fastify.get('/another/path', function (req, reply) {
  reply.sendFile('myHtml.html') // serving path.join(__dirname, 'public', 'myHtml.html') directly
})

fastify.get('/another/patch-async', async function (req, reply) {
  return reply.sendFile('myHtml.html')
})

fastify.get('/path/with/different/root', function (req, reply) {
  reply.sendFile('myHtml.html', path.join(__dirname, 'build')) // serving a file from a different root location
})

fastify.get('/another/path', function (req, reply) {
  reply.sendFile('myHtml.html', { cacheControl: false }) // overriding the options disabling cache-control headers
})

// Run the server!
fastify.listen({ port: 3000 }, (err, address) => {
  if (err) throw err
  // Server is now listening on ${address}
})

Multiple prefixed roots

const fastify = require('fastify')()
const fastifyStatic = require('@fastify/static')
const path = require('node:path')
// first plugin
fastify.register(fastifyStatic, {
  root: path.join(__dirname, 'public')
})

// second plugin
fastify.register(fastifyStatic, {
  root: path.join(__dirname, 'node_modules'),
  prefix: '/node_modules/',
  decorateReply: false // the reply decorator has been added by the first plugin registration
})

Sending a file with content-disposition header

const fastify = require('fastify')()
const path = require('node:path')

fastify.register(require('@fastify/static'), {
  root: path.join(__dirname, 'public'),
  prefix: '/public/', // optional: default '/'
})

fastify.get('/another/path', function (req, reply) {
  reply.download('myHtml.html', 'custom-filename.html') // sending path.join(__dirname, 'public', 'myHtml.html') directly with custom filename
})

fastify.get('another/patch-async', async function (req, reply) {
  // an async handler must always return the reply object
  return reply.download('myHtml.html', 'custom-filename.html')
})

fastify.get('/path/without/cache/control', function (req, reply) {
  reply.download('myHtml.html', { cacheControl: false }) // serving a file disabling cache-control headers
})

fastify.get('/path/without/cache/control', function (req, reply) {
  reply.download('myHtml.html', 'custom-filename.html', { cacheControl: false })
})

Options

root (required)

The absolute path of the directory that contains the files to serve. The file to serve will be determined by combining req.url with the provided root directory.

You can also provide an array of directories containing files to serve. This is useful for serving multiple static directories under a single prefix. Files are served in a "first found, first served" manner, so the order in which you list the directories is important. For best performance, you should always list your main asset directory first. Duplicate paths will raise an error.

prefix

Default: '/'

A URL path prefix used to create a virtual mount path for the static directory.

constraints

Default: {}

Constraints that will be added to registered routes. See Fastify's documentation for route constraints.

prefixAvoidTrailingSlash

Default: false

If set to false prefix will get trailing "/" at the end. If set to true, prefix will not append "/" to prefix.

schemaHide

Default: true

A flag that define if the fastify route hide-schema attribute is hidden or not

setHeaders

Default: undefined

A function to set custom headers on the response. Alterations to the headers must be done synchronously. The function is called as fn(res, path, stat), where the arguments are:

  • res The response object.
  • path The path of the file that is being sent.
  • stat The stat object of the file that is being sent.

send Options

The following options are also supported and will be passed directly to the send module:

You're able to alter this options when calling reply.download('filename.html', options) or reply.download('filename.html', 'otherfilename.html', options) on each response to a request.

redirect

Default: false

If set to true, @fastify/static redirects to the directory with a trailing slash.

This option cannot be set to true with wildcard set to false on a server with ignoreTrailingSlash set to true.

If this option is set to false, then requesting directories without trailing slash will trigger your app's 404 handler using reply.callNotFound().

wildcard

Default: true

If set to true, @fastify/static adds a wildcard route to serve files. If set to false, @fastify/static globs the filesystem for all defined files in the served folder (${root}/**/**), and just creates the routes needed for those and it will not serve the newly added file on the filesystem.

The default options of https://www.npmjs.com/package/glob are applied for getting the file list.

This option cannot be set to false with redirect set to true on a server with ignoreTrailingSlash set to true.

allowedPath

Default: (pathName, root, request) => true

This function allows filtering the served files. Also, with the help of the request object a more complex path authentication is possible. If the function returns true, the file will be served. If the function returns false, Fastify's 404 handler will be called.

index

Default: undefined

Under the hood we use send lib that by default supports "index.html" files. To disable this set false or to supply a new index pass a string or an array in preferred order.

serveDotFiles

Default: false

When true, files in hidden directories (e.g. .foo) will be served.

list

Default: undefined

If set, it provides the directory list calling the directory path.

Default response is json.

Note:

  • Multi-root is not supported within the list option.
  • If dotfiles option value is deny or ignore, dotfiles will be excluded.

Example:

fastify.register(require('@fastify/static'), {
  root: path.join(__dirname, 'public'),
  prefix: '/public/',
  index: false
  list: true
})

Request

GET .../public

Response

{ "dirs": ["dir1", "dir2"], "files": ["file1.png", "file2.txt"] }

list.format

Default: json

Options: html, json

Directory list can be also in html format; in that case, list.render function is required.

You can override the option with URL parameter format. Options are html and json.

GET .../public/assets?format=json

will return the response as json independent of list.format.

Example:

fastify.register(require('@fastify/static'), {
  root: path.join(__dirname, 'public'),
  prefix: '/public/',
  list: {
    format: 'html',
    render: (dirs, files) => {
      return `
<html><body>
<ul>
  ${dirs.map(dir => `<li><a href="${dir.href}">${dir.name}</a></li>`).join('\n  ')}
</ul>
<ul>
  ${files.map(file => `<li><a href="${file.href}" target="_blank">${file.name}</a></li>`).join('\n  ')}
</ul>
</body></html>
`
      },
  }
})

Request

GET .../public

Response

<html><body>
<ul>
  <li><a href="/dir1">dir1</a></li>
  <li><a href="/dir1">dir2</a></li>
</ul>
<ul>
  <li><a href="/foo.html" target="_blank">foo.html</a></li>
  <li><a href="/foobar.html" target="_blank">foobar.html</a></li>
  <li><a href="/index.css" target="_blank">index.css</a></li>
  <li><a href="/index.html" target="_blank">index.html</a></li>
</ul>
</body></html>

list.names

Default: ['']

Directory list can respond to different routes, declared in list.names options.

Note: if a file with the same name exists, the actual file is sent.

Example:

fastify.register(require('@fastify/static'), {
  root: path.join(__dirname, '/static'),
  prefix: '/public',
  prefixAvoidTrailingSlash: true,
  list: {
    format: 'json',
    names: ['index', 'index.json', '/']
  }
})

Dir list respond with the same content to

GET .../public
GET .../public/
GET .../public/index
GET .../public/index.json

list.extendedFolderInfo

Default: undefined

If true some extended information for folders will be accessible in list.render and in the json response.

render(dirs, files) {
  const dir = dirs[0];
  dir.fileCount // number of files in this folder
  dir.totalFileCount // number of files in this folder (recursive)
  dir.folderCount // number of folders in this folder
  dir.totalFolderCount // number of folders in this folder (recursive)
  dir.totalSize // size of all files in this folder (recursive)
  dir.lastModified // most recent last modified timestamp of all files in this folder (recursive)
}

Warning: This will slightly decrease the performance, especially for deeply nested file structures.

list.jsonFormat

Default: names

Options: names, extended

This option determines the output format when json is selected.

names:

{
  "dirs": [
    "dir1",
    "dir2"
  ],
  "files": [
    "file1.txt",
    "file2.txt"
  ]
}

extended:

{
  "dirs": [
    {
      "name": "dir1",
      "stats": {
        "dev": 2100,
        "size": 4096,
        ...
      },
      "extendedInfo": {
        "fileCount": 4,
        "totalSize": 51233,
        ...
      }
    }
  ],
  "files": [
    {
      "name": "file1.txt",
      "stats": {
        "dev": 2200,
        "size": 554,
        ...
      }
    }
  ]
}

preCompressed

Default: false

Try to send the brotli encoded asset first (when supported within the Accept-Encoding headers), retry for gzip, then the fall back to the original pathname. You may choose to skip compression for smaller files that don't benefit from it.

Assume this structure with the compressed asset as a sibling of the un-compressed counterpart:

./public
├── main.js
├── main.js.br
├── main.js.gz
├── crit.css
├── crit.css.gz
└── index.html

Disable serving

If you would just like to use the reply decorator and not serve whole directories automatically, you can simply pass the option { serve: false }. This will prevent the plugin from serving everything under root.

Disabling reply decorator

The reply object is decorated with a sendFile function by default. If you want to disable this, pass the option { decorateReply: false }. If @fastify/static is registered to multiple prefixes in the same route only one can initialize reply decorators.

Handling 404s

If a request matches the URL prefix but a file cannot be found for the request, Fastify's 404 handler will be called. You can set a custom 404 handler with fastify.setNotFoundHandler().

When registering @fastify/static within an encapsulated context, the wildcard option may need to be set to false in order to support index resolution and nested not-found-handler:

const app = require('fastify')();

app.register((childContext, _, done) => {
    childContext.register(require('@fastify/static'), {
        root: path.join(__dirname, 'docs'), // docs is a folder that contains `index.html` and `404.html`
        wildcard: false
    });
    childContext.setNotFoundHandler((_, reply) => {
        return reply.code(404).type('text/html').sendFile('404.html');
    });
    done();
}, { prefix: 'docs' });

This code will send the index.html for the paths docs, docs/, and docs/index.html. For all other docs/<undefined-routes> it will reply with 404.html.

Handling Errors

If an error occurs while trying to send a file, the error will be passed to Fastify's error handler. You can set a custom error handler with fastify.setErrorHandler().

Payload stream.filename

If you need to access the filename inside the onSend hook, you can use payload.filename.

fastify.addHook('onSend', function (req, reply, payload, next) {
  console.log(payload.filename)
  next()
})

License

Licensed under MIT

fastify-static's People

Contributors

allevo avatar cameronbraid avatar cemremengu avatar climba03003 avatar coreyfarrell avatar dependabot-preview[bot] avatar dependabot[bot] avatar dwickern avatar eomm avatar fdawgs avatar frikille avatar gj avatar greenkeeper[bot] avatar gurgunday avatar is2ei avatar jelenkee avatar jsumners avatar mav-rik avatar mcollina avatar mse99 avatar mylesborins avatar nigelhanlon avatar nwoltman avatar olmesm avatar rafaelgss avatar rluvaton avatar ryhinchey avatar salmanm avatar uzlopak avatar wight554 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

fastify-static's Issues

Fastify-static http2/https typescript warning

When using fastify with https/http2 like this:

import fastifyStatic from 'fastify-static'

const https: fastify.FastifyInstance<Http2SecureServer, Http2ServerRequest, Http2ServerResponse> = fastify({
  logger: log,
  ignoreTrailingSlash: true,
  http2: true,
  https: { allowHTTP1: true }
})

https.register(fastifyStatic, { root: path.resolve(__dirname, '../../public'), prefix: '/public/' })

You will get typescript warning:

error TS2345: Argument of type '(instance: FastifyInstance<Server, IncomingMessage, ServerResponse>, options: { root: string; prefix?: string | undefined; ... 14 more ...; maxAge?: string | ... 1 more ... | undefined; }, callback: (err?: FastifyError | undefined) => void) => void' is not assignable to parameter of type '(instance: FastifyInstance<Http2SecureServer, Http2ServerRequest, Http2ServerResponse>, options: { ...; }, callback: (err?: FastifyError | undefined) 
=> void) => void'.
  Types of parameters 'instance' and 'instance' are incompatible.
    Type 'FastifyInstance<Http2SecureServer, Http2ServerRequest, Http2ServerResponse>' is not assignable to type 'FastifyInstance<Server, IncomingMessage, ServerResponse>'.
      Types of property 'server' are incompatible.
        Type 'Http2SecureServer' is not assignable to type 'Server'.

What is the reason of this?

How can i get my image that saved in server?

Hello, can i get my image that saved in my server?
so i saved my image at public in root folder, can i get it and show it in my frontend app?
Thank you

this give me an error

fastify.register(require('fastify-static'), {
root: path.join(__dirname)
})

fastify.get('*', function (req, reply) {
    reply.sendFile('client/build', 'index.html')
})

fastify.get('/api/gambar', function (req, reply) {
    reply.sendFile('public/gambar', 'noimage.png')
})

Sporadic ERR_STREAM_PREMATURE_CLOSE

🐛 Bug Report

Seemingly at random, fastify-static will fail to serve a static file.

  • Refreshing/re-requesting the file usually fixes it
  • Happens for text responses and binary files such as PNGs
  • Happens with or without fastify-compress

Stack trace:

Error: Premature close
    at PassThrough.onclose ([redacted]/node_modules/fastify/node_modules/readable-stream/lib/internal/streams/end-of-stream.js:62:73)
    at PassThrough.emit (events.js:215:7)
    at emitCloseNT ([redacted]/node_modules/fastify-static/node_modules/readable-stream/lib/internal/streams/destroy.js:56:8)
    at processTicksAndRejections (internal/process/task_queues.js:79:21)

Error log in console:

{
  "level": 50,
  "time": 1574780438858,
  "pid": 12747,
  "hostname": "[redacted]",
  "reqId": 12,
  "req": {
    "method": "GET",
    "url": "/sa/Home_qVN1-2b1b950.js",
    "remotePort": 52715
   },
   "res": { "statusCode": 500 },
   "err" :{
     "type": "NodeError",
     "message": "Premature close",
     "stack": "Error: Premature close\n    at PassThrough.onclose ([redacted]/node_modules/fastify/node_modules/readable-stream/lib/internal/streams/end-of-stream.js:62:73)\n    at PassThrough.emit (events.js:215:7)\n    at emitCloseNT ([redacted]/node_modules/fastify-static/node_modules/readable-stream/lib/internal/streams/destroy.js:56:8)\n    at processTicksAndRejections (internal/process/task_queues.js:79:21)",
     "name": "Error",
     "code": "ERR_STREAM_PREMATURE_CLOSE"
    },
  "msg": "Premature close",
  "v": 1
}

To Reproduce

Steps to reproduce the behavior:

  1. Serve some static files with fastify-static
  2. Seemingly randomly, the above will happen for page assets
  3. The file indeed never makes it to the browser

Paste your code here:

import path from 'path'
import serveStatic from 'fastify-static'
import fp from 'fastify-plugin'

function staticAssets (server, opts, next) {
  server.register(serveStatic, {
    root: path.join(__dirname, '../browser'),
    prefix: '/sa/',
    maxAge: 9999999,
    immutable: true,
    dotfiles: 'ignore',
    index: false,
    decorateReply: false
  })

  server.register(serveStatic, {
    root: path.join(__dirname, '../server/magic-urls'),
    maxAge: 24 * 60 * 60, // 1 day
    dotfiles: 'ignore',
    index: false,
    decorateReply: false
  })
  next()
}

export default fp(staticAssets, {
  fastify: '2.10',
  name: '[redacted]-static-assets'
})

This plugin is then registered like so:

import staticAssets from './static-assets'

fastify.register(staticAssets)

Expected behavior

A clear and concise description of what you expected to happen.

Paste the results here:

Your Environment

  • node version: 12.13.0
  • fastify version: 2.10.0
  • os: MacOS Mojave 10.14.6
  • Requesting assets with MacOS Firefox 71, but it doesn’t appear to be doing anything special in its devtools

How to use a Layout ?

Hi,
I would like to know if it's possible to use a Layout with Fastify-Static.

  • Every page have the same header
  • Every page have the same footer
  • Every page have the same style and layout

Can you share a solution or it's not possible ?

Thank you

dynamic reply

Is able to sendFile work with dynamic content from buffer?
I want to send file without saving on disk

Feature Request: Option to ignore ENOENT in checkRootPathForErrors

The npm start for one of my web apps runs a fastify server which uses fastify-static twice:

fastify
  .register(fastifyStatic, {
    prefix: '/dev/',
    root: path.resolve('src')
  })
  .register(fastifyStatic, {
    prefix: '/app/',
    root: path.resolve('dist')
  })

The src directory always exists, this is the unmodified source. dist may or may not exist, it is the product of gulp build. Right now I use fs.mkdirSync(path.resolve('dist'), {recursive: true}) before initializing the fastifyStatic plugin to suppress the "root" path "${rootPath}" must exist error. It would be nice if I did not have to force creation of the empty folder for this to work, just let normal 404 logic deal with ENOENT at request time.

Are you open to this?

Feature Request: Add the ability to serve a folder directly from the terminal

It should have the ability to serve a folder directly from the terminal.

For example:
Lets say I installed fastify-static globally with -g flag, now I should be able to serve a folder with below commant
fastify-static . //serve current folder
fastify-static app/public/ -p 80 //serve the folder app/pblic on post 80

Version 10 of node.js has been released

Version 10 of Node.js (code name Dubnium) has been released! 🎊

To see what happens to your code in Node.js 10, Greenkeeper has created a branch with the following changes:

  • Added the new Node.js version to your .travis.yml

If you’re interested in upgrading this repo to Node.js 10, you can open a PR with these changes. Please note that this issue is just intended as a friendly reminder and the PR as a possible starting point for getting your code running on Node.js 10.

More information on this issue

Greenkeeper has checked the engines key in any package.json file, the .nvmrc file, and the .travis.yml file, if present.

  • engines was only updated if it defined a single version, not a range.
  • .nvmrc was updated to Node.js 10
  • .travis.yml was only changed if there was a root-level node_js that didn’t already include Node.js 10, such as node or lts/*. In this case, the new version was appended to the list. We didn’t touch job or matrix configurations because these tend to be quite specific and complex, and it’s difficult to infer what the intentions were.

For many simpler .travis.yml configurations, this PR should suffice as-is, but depending on what you’re doing it may require additional work or may not be applicable at all. We’re also aware that you may have good reasons to not update to Node.js 10, which is why this was sent as an issue and not a pull request. Feel free to delete it without comment, I’m a humble robot and won’t feel rejected 🤖


FAQ and help

There is a collection of frequently asked questions. If those don’t help, you can always ask the humans behind Greenkeeper.


Your Greenkeeper Bot 🌴

Set content type: The character encoding of the plain text document was not declared

Whe using http2: true with fastify, I get:

The character encoding of the plain text document was not declared. The document will render with garbled text in some browser configurations if the document contains characters from outside the US-ASCII range. The character encoding of the file needs to be declared in the transfer protocol or file needs to use a byte order mark as an encoding signature.

How do I set the content type for files?
Right now I serve my test.html file like this (it test the api):

fastify.register(require('fastify-static'), {
  root: path.join(__dirname, '/public'),
  prefix: '/public/', // optional: default '/'
})

Thanks!

Support preRequest hooks

🚀 Feature Proposal

Support preRequest hooks, or a middleware like option, for intercepting before handling serving statics.

Motivation

For cases like, filter files, or serve statics depends on request attributes. In my case, I want to serve .map file only to developer instead of all end users.

We could implement this with a hook, or middleware, or something else?

Type link directly return 404 error

I've error when i typed my link directly like this localhost:5000/katalog, it send me error 404. But if i move from link to link using (React Router Dom) it doesn't show me error.
Here's my screen recorder of this behaviour Youtube Video.
Why this happened?

Can no longer use the catch all route

fastify.register(require('fastify-static'), {
  root: path.join(__dirname, 'public'),
})

// the catch all route below will never be called 😥
fastify.get('*', function (request, reply) {
  console.log('Catch all')
  reply.send('*')
})

Several directory

Hello, how i can set several directory? I didn't see this example in doc.

try

fastify.register( require('fastify-static'), { root: path.join(__dirname, 'dist'), prefix: '/dist/' } )
fastify.register( require('fastify-static'), { root: path.join(__dirname, 'public'), prefix: '/public/' } )

Error

node_modules\fastify\lib\decorate.js:19
throw new FST_ERR_DEC_ALREADY_PRESENT(name)

will great see array format

[
{ root: path.join(__dirname, 'dist1'), prefix: '/dist1/' },
{ root: path.join(__dirname, 'dist2'), prefix: '/dist2/' },
{ root: path.join(__dirname, 'dist3'), prefix: '/dist3/' }  
]

Multiple instances

How to declare plugin twice ?

fastify.register(require('fastify-static'), {
  root: path.join(__dirname, 'landing/public'),
  prefix: '/landing/', // optional: default '/'
})

fastify.register(require('fastify-static'), {
  root: path.join(__dirname, 'admin/dist'),
  prefix: '/admin/', // optional: default '/'
})

async style not supported

🐛 Bug Report

When async style is used, undefined promise error is always logged.
Originally filed in as help issue 106

To Reproduce

Steps to reproduce the behavior:

Change the example in in project to use async and watch logs.

'use strict';

const path = require('path');
const fastify = require('fastify')({ logger: { level: 'trace' } });

fastify
  .register(require('../'), { root: path.join(__dirname, '/public') })
  .get('/', async (request, reply) => {
    reply.sendFile('index.css');
  });

fastify.listen(3000, err => {
  if (err) throw err;
});

Expected behavior

No promise undefined error to be logged as per Promise resolution documentation.

Your Environment

  • node version: 12.7.0
  • fastify version: 2.8.0
  • os: Mac

Option to redirect requests for directory.

Take the following files within the root directory:

index.html
sub/index.html

Configure fastify-static to include index: 'index.html'. In this case http://localhost/ serves the root index.html, http://localhost/sub/ serves sub/index.html. But http://localhost/sub is a 404 error. Would you accept an option that would hook send.on('directory') to perform a 301 redirect of such requests to the path including the trialing slash? Such that http://localhost/sub would be 301 with Location: http://localhost/sub/. I haven't tested this yet but the send documentation tells how to accomplish this. I'm assuming that directory redirects would need to be opt-in rather than opt-out? Either would be fine with me, just figured I'd ask since fastify-static is in the middle of a semver-major release.

setNotFoundHandler not triggering hooks

🐛 Bug Report

The docs for setNotFoundHandler specify it's possible to use hooks to be triggered before the handler is triggered:

You can also register a preValidation and preHandler hook for the 404 handler.

When using fastify-static these hooks are not being triggered.

To Reproduce

If you run this code you will see the preHandler hook is not being triggered. Disabling the fastify-static plugin solves the problem.

const path = require('path');

const fastify = require('fastify')({
	logger: true
});

fastify.register(require('fastify-static'), {
	root: path.join(__dirname, '/static/')
});

fastify.setNotFoundHandler({
	preHandler: (request, reply, next) => {
		console.log('preHandler!');
		next();
	}
}, function (request, reply) {
	reply.status(404);
	reply.send();
});

fastify.listen(5555, '0.0.0.0', function (err, address) {
	if (err) {
		fastify.log.error(err)
		process.exit(1)
	}
	fastify.log.info(`server listening on ${address}`)
});

Expected behavior

When the setNotFoundHandler callback is triggered it should also trigger the hooks defined for this handler.

Your Environment

  • node version: 12
  • fastify version: >=2.0.0
  • os: Mac

Not working with svelte/sapper middleware

🐛 Bug Report

Using fastify-static plugin with sapper middleware gives 404 on any static file

To Reproduce

Follow these steps: https://www.matthewcantelon.ca/blog/sapper-fastify/
The static files are not served (it gives 404) while it works with sirv

fastify.use(sirv('static'))

Without the sapper middleware, it works
Would it be an issue with the plugin not doing sth at the end that let the middleware "break"?

Expected behavior

The static files should be served

Your Environment

  • node version: v13.2.0
  • "fastify": "^2.10.0"
  • "fastify-static": "^2.5.1"
  • "sirv": "^0.4.2"
  • os: Linux
  • any other relevant information

An in-range update of fastify is breaking the build 🚨

Version 1.11.0 of fastify was just published.

Branch Build failing 🚨
Dependency fastify
Current Version 1.10.0
Type devDependency

This version is covered by your current version range and after updating it in your project the build failed.

fastify is a devDependency of this project. It might not break your production code or affect downstream projects, but probably breaks your build or test tools, which may prevent deploying or publishing.

Status Details
  • continuous-integration/travis-ci/push: The Travis CI build failed (Details).

Release Notes v1.11.0

Features

  • Added pluginTimeout option - #1088

Internals

  • Override address in listen instead of listenPromise - #1102
  • Rename and call unknown method tests from http2 - #1095
  • Ensure that error-in-post test is run - #1096
  • Add test for trust proxy with function - #1098

Documentation

  • Move Server-Methods into Factory - #1101
  • Update Validation-and-Serialization.md - #1094
Commits

The new version differs by 11 commits.

  • c62ace8 Bumped v1.11.0
  • acf3950 Override address in listen instead of listenPromise (#1102)
  • e6bca66 Move Server-Methods into Factory (#1101)
  • ba9a629 Update Validation-and-Serialization.md (#1094)
  • e77cae9 Add test for trust proxy with function (#1098)
  • 6bce249 Ensure that error-in-post test is run (#1096)
  • 3895a75 Rename and call unknown method tests from http2 (#1095)
  • 1f750e6 Ignore pino in greenkeeper, it's a semver-major change
  • 28436d1 chore(package): update concurrently to version 4.0.0 (#1091)
  • a258482 Update TypeScript to 3.0.1 (#1092)
  • 50bcf6b Added pluginTimeout option. (#1088)

See the full diff

FAQ and help

There is a collection of frequently asked questions. If those don’t help, you can always ask the humans behind Greenkeeper.


Your Greenkeeper Bot 🌴

reply.notFound is not a function

Hello,

I use fastify-static 0.6.0 with fastify 1.2.1. When I try to call an unknown static file, I don't receive any 404 error, and my application crashed after this error :

TypeError: reply.notFound is not a function
at SendStream. (/var/www/node_modules/fastify-static/index.js:42:15)
at emitOne (events.js:116:13)
at SendStream.emit (events.js:211:7)
at SendStream.error (/var/www/node_modules/send/index.js:270:17)
at SendStream.onStatError (/var/www/node_modules/send/index.js:421:12)
at onstat (/var/www/node_modules/send/index.js:727:26)
at FSReqWrap.oncomplete (fs.js:152:21)

Using fastify-compression breaks Response time for static assets on browser

Config
fastify.register(require('fastify-compress'), { threshold: 0 });
fastify.register(fastifyStatic, __PROD__ ? { root: path.resolve(path.join(__dirname, '/../', 'public')), prefix: '/public/', setHeaders: function (res, path, stat) { res.setHeader('Content-Encoding', 'gzip') // console.log(stat) } }
tracelog on server
{"level":30,"time":1524127887660,"msg":"incoming request","pid":11241,"hostname":"utkarsh-Lenovo-ideapad-300-15ISK","reqId":9,"req":{"id":9,"method":"GET","url":"/public/assets/15_e8d859185f2e2b063a5c.js","remoteAddress":"127.0.0.1","remotePort":37776},"v":1}
{"level":30,"time":1524127887668,"msg":"request completed","pid":11241,"hostname":"utkarsh-Lenovo-ideapad-300-15ISK","reqId":9,"res":{"statusCode":200},"responseTime":7.410842999815941,"v":1}

Browser
image

PS Request is being made on localhost and if I remove compression it works properly but without compression ofcourse.

Don't persist path error on startup for forever

When I initialize fastify-plugin in folder /static and the path does not exists 404 error is thrown. When I create the folder /static after that the error still persists and the user doesn't know what's going on. We don't have to persist that error the path is already checked by send module and can be handled by the error events.

btw: Isn't possible to use serve-static as middleware ?

folders without trailing slash hang fastify

i'm using the below to select the public directory

fastify.register(require('fastify-static'), {
  root: path.join(__dirname, 'public')
});

inside public is index.html & 3 folders js, css & img as expected

if i browse to:
/css/stylesheet.css --> I get the correct sheet and it works OK
/css/ --> returns 404 which is expected
/css --> fastify hangs and does not return a response and the browser sits waiting for a response

Testing with Google Chrome & Microsoft Edge and both have the same issue

Optional trailing "/" after prefix

🐛 Bug Report

I am trying to serve static content over /docs prefix, but being redirected to /docs/ since trailing slash is appended by default.

To Reproduce

Register route with prefix that does not have trailing /, start application, assuming everything done right static content in docs folder will be available at /docs/ instead of /docs.

app.register(fastifyStatic, {
    root: path.join(__dirname, 'docs'),
    prefix: '/docs'
  });

Expected behavior

Static content be available at /docs instead of /docs/.

Your Environment

  • node version: 12
  • fastify version: 2.13.1
  • os: Linux

render static file with route parameter

I am using plugin ponit-of-view and fastify-static, I have a route for example : fastify.get('/', function (request, reply) { reply.view('/index.ejs', { text: 'text' }); }) in the file index.ejs, I am importing a css file and js, it is work perfect.

But when I change route for this example: fastify.get('/:dynimic/route', function (request, reply) { reply.view('/index.ejs', { text: 'text' }); })
I have a problem, see below.
GET http://localhost:3000/astes/css/css.css net::ERR_ABORTED GET http://localhost:3000/astes/js/console.js net::ERR_ABORTED.

provide clearer error if "root" does not exist

If root points to a non-existing directory the error printed is:

Error: ENOENT: no such file or directory, stat '/Users/username/project/some-directory'
    at statSync (fs.js:851:3)

Instead, maybe we can print "please verify the specified "root" directory exists"?

Inability to serve multiple prefixed root URL's

For a development server I'm working on I tried the following:

const path = require('path');
const fastify = require('fastify')();
const fastifyStatic = require('fastify-static');

fastify.register(fastifyStatic, {
	root: path.join(__dirname, 'node_modules'),
	prefix: '/node_modules/',
});

fastify.register(fastifyStatic, {
	root: path.join(__dirname, 'html/myapp'),
	prefix: '/myapp/',
});

This causes an exception from node_modules/fastify/lib/decorate.js:38:
Error: The decorator 'sendFile' has been already added to Reply!

Is it possible to fix this within fastify-static or should I write my own middleware that uses send directly?

reply is file

hello, how i can send file?
In example

const fastify = require('fastify')({
    logger: false,
    http2: true,
    https: {
        key: fs.readFileSync(path.join(__dirname, '/certificate/localhost/', 'key.pem')),
        cert: fs.readFileSync(path.join(__dirname, '/certificate/localhost/', 'cert.pem'))
    }
});

fastify.register(require('fastify-favicon'));
fastify.register(require('fastify-cookie'))
//STATIC
fastify.register( require('fastify-static'), { root: path.join(__dirname, 'dist'), prefix: '/dist/', decorateReply: false } )
fastify.register( require('fastify-static'), { root: path.join(__dirname, 'public'), prefix: '/public/', decorateReply: false } )

fastify.get('/another/path', function (req, reply) {
  reply.sendFile('myHtml.html') // serving path.join(__dirname, 'public', 'myHtml.html') directly
})

try

fastify.get('/robots.txt', function (req, reply) {
    reply.sendFile( path.join( __dirname, '/public/', 'robots.txt' ) )
})

error

reply.sendFile( path.join( __dirname, '/public/', 'robots.txt' ) )
          ^
TypeError: reply.sendFile is not a function

server http2, whats wrong?

HTTP2 / Connection refused / 304 response

I recently started to use Fastify to have http2 support.

I just noticed if I reload my browser with caching enabled (so not using Chrome DevTools 'Disable Cache' option) almost every time I have a "Connection refused / 304" error and my asset is not loaded.

I'm running it on localhost now, using a self-signed certificate, added the certificate as an Always Trusted cert. I'm using Chrome 70.0.3538.110 on OSX.

image

Once I turn off the http2 in Fastify everything works fine.

image

I was wondering if could it be because http2 push is used? I was trying to simply have http2 protocol and static asset serving, i can live without http2 push. (it show the same symptoms as the node-auto-push, which I tried before fastify-static)

I have this error (using pm2) in the logs:

TypeError: Cannot read property 'Symbol(trailers)' of undefined
  at ServerHttp2Stream.onStreamTrailersReady (internal/http2/compat.js:377:36)
  at ServerHttp2Stream.emit (events.js:182:13)
  at Http2Stream.onStreamTrailers [as ontrailers] (internal/http2/core.js:311:15)

My index.js looks like this (removed unnecessary parts):

const fastify = require('fastify')({
  logger: {
    prettyPrint: DEBUG,
  },
  trustProxy: false,
  http2: true,
  https: {
    allowHTTP1: true,
    key: fs.readFileSync(path.join(__dirname, '.', 'https', SSLKey)),
    cert: fs.readFileSync(path.join(__dirname, '.', 'https', SSLCert)),
  },
});

const helmet = require('fastify-helmet');
const fastifyPug = require('fastify-pug');

const assetRoot = path.join(__dirname, '..', 'client', 'assets');

fastify.register(require('fastify-static'), {
  root: assetRoot,
});

fastify.register(require('fastify-compress'), {
  global: true,
});

fastify.register(helmet, {
  contentSecurityPolicy: {
    directives: {
      defaultSrc: ['\'self\''],
      styleSrc: ['\'self\'', '\'unsafe-inline\''],
      scriptSrc: ['\'self\'', '\'unsafe-inline\''],
    },
  },
  featurePolicy: {
    features: {
      fullscreen: ['\'none\''],
      payment: ['\'none\''],
      syncXhr: ['\'none\''],
      microphone: ['\'none\''],
    },
  },
  frameguard: {
    action: 'deny',
  },
  hidePoweredBy: { setTo: 'Sun and love <3' },
  referrerPolicy: { policy: 'same-origin' },
});
fastify.register(fastifyPug, { views: `${path.join(__dirname)}/views` });

fastify.get('/', (request, reply) => {
  reply
    .code(200)
    .render('index.pug');
});

fastify.listen(SECUREPORT, HOST, (err) => {
  if (err) {
    fastify.log.error(err);
    process.exit(1);
  }
});

I'm stuck now, but really don't want to revert to http1.1 or to manually serve assets.
Can the Node 10 and its experimental http2 feature the cause?

Thanks!

An in-range update of request is breaking the build 🚨

☝️ Greenkeeper’s updated Terms of Service will come into effect on April 6th, 2018.

Version 2.84.0 of request was just published.

Branch Build failing 🚨
Dependency request
Current Version 2.83.0
Type devDependency

This version is covered by your current version range and after updating it in your project the build failed.

request is a devDependency of this project. It might not break your production code or affect downstream projects, but probably breaks your build or test tools, which may prevent deploying or publishing.

Status Details
  • continuous-integration/travis-ci/push The Travis CI build failed Details

Commits

The new version differs by 6 commits.

  • d77c839 Update changelog
  • 4b46a13 2.84.0
  • 0b807c6 Merge pull request #2793 from dvishniakov/2792-oauth_body_hash
  • cfd2307 Update hawk to 7.0.7 (#2880)
  • efeaf00 Fixed calculation of oauth_body_hash, issue #2792
  • 253c5e5 2.83.1

See the full diff

FAQ and help

There is a collection of frequently asked questions. If those don’t help, you can always ask the humans behind Greenkeeper.


Your Greenkeeper Bot 🌴

Support for static compression?

Not sure if this is the right place to ask but is it possible to support static compressed files: For example if a request is received for http://localhost/bundle.js with Accept-Encoding: brotli, I want to serve the already compressed bundle.js.br instead of compressing bundle.js on the fly. Same if gzip compression is requested, I'd want fastify-static to send bundle.js.gz if it exists. For my goal if the pre-compressed file does not exist then I'd want to send the uncompressed file, lack of pre-compressed file would mean that the build system determined that the compressed file was actually larger.

Am I correct that for fastify-static to support this it would need to be supported by send? They have a PR to add this feature but it appears to have stalled.

percent-encoded URLs might fail in glob mode

🐛 Bug Report

When trying to access a URL containing (for example) %20 to indicate a space in the filename with fastify-static configured with wildcard: false:

  • if only the file with a space in its name is present, 404 is returned
  • if only a file with %20 in its name is present, 404 is returned
  • if both a file containing a space in its name and a file containing %20 in its name is present, the file containing the space in its name is served.

To Reproduce

Steps to reproduce the behavior:

var  fastify = (require('fastify'))({});

fastify.register(require('fastify-static'), {
  root: __dirname,
  prefix: '/',
  wildcard: false
});

fastify.listen().then(console.log);
touch 'a .md'

Start JS script, try to get /a%20.md: 404

touch 'a%20.md'

Start JS script, try to get /a%20.md: OK

rm 'a .md'

Start JS script, try to get /a%20.md: 404

Expected behavior

Since the file containing the space in its name is served properly, it should be available as containing %20 in its name. This means glob should use percent-encoded URLs for non-unreserved characters (as defined in RFC3986).

Your Environment

  • node version: 14
  • fastify version: 3.4.1
  • os: Linux

Server Side Caching

Hi,
Is there any way to configure a server side caching of static assets?
Using something like fastify-caching.
PS: I have deployed fastify using docker containers so I cannot rely on iobuffer performance as it is being shared by multiple apps.

Feature Request: sendFile without serving anything

On my site each user has subdomain:

ilya.mysite.com/his/file
homer.mysite.com/index.html

I can't just tell serve this folder, I first retrieve username (homer), then serve his folder (users/homer). Ideally I would like to use reply.sendFile(path), but couldn't do it without specifying root folder (and serving file I don't want to server). Would be nice to have reply.sendFile separately from everything else.

Thanks!

setNotFoundHandler is broken with Fastify v2

I checked a bit and couldn't understand what the problem might be. I played around with the tests of setNotFoundHandler in main Fastify repo and they seem to be good.

Any ideas what might be wrong?

https://github.com/fastify/fastify-static/blob/master/test/static.test.js#L362

  not found responses can be customized with fastify.setNotFoundHandler() > /path/does/not/exist.html
  not ok should be equal
    +++ found
    --- wanted
    -text/plain
    +application/json; charset=utf-8
    compare: ===

  not found responses can be customized with fastify.setNotFoundHandler() > /path/does/not/exist.html
  not ok should be equal
    +++ found
    --- wanted
    -/path/does/not/exist.html Not Found
    +{"statusCode":404,"code":"ENOENT","error":"Not Found","message":"ENOENT: no such file or directory, stat 'C:\\Users\\cemre.mengu\\Desktop\\fastify-static\\test\\static\\path\\does\\not\\exist.html'"}
    compare: ===

Reloading a static page served by fastify-static gets stuck

In my test app, which simply uses fastify-static for serving index.html and some css files, etc, the page loads fine the first time. But pressing the reload button gets stuck. Chrome just shows the spinner on the tab, displaying nothing. It looks like the index.html file is served fine but requests for all other resources are pending forever.

If I clear the cache and the cookies, the page loads again fine. But only the first time. The same issue afterwards.

My env:

  • fastify: 1.0.0
  • fastify-static: 0.10.0
  • using http2 with tls-keygen-generated certificates

Proposal: dir index

🚀 Feature Proposal

I'd like to implement a feature to get the "index" of the directory, if the request is the dir root or "index" or "index.html" - all of that optional and configurable. Output format can be json or html; for html a string for template is required.
Next improvement could optionally add files stats to output

Motivation

Can be useful to have an index for dir navigation or to quickly expose files

Example

A draft example could be

json

fastify.register(require('fastify-static'), {
  root: path.join(__dirname, 'public'),
  prefix: '/public/',
  index: { // optional
    format: 'json', // optional, default json (?), can be both ?
    names: ['/index', '/index.json', '/'] // optional, default '/'
  }
})

request
GET .../public/images or GET .../public/images/index.json

response

{
  "dirs": ["house", "office"],
  "files": ["bed.jpg", "kitchen.png"]
}

html

fastify.register(require('fastify-static'), {
  root: path.join(__dirname, 'public'),
  prefix: '/public/',
  index: { 
    format: 'html',
    template: '<html> ... TDB ... </html>',
    names: ['/index', '/index.html', '/'] // optional, default '/'
  }
})

request
GET .../public/images or GET .../public/images/index.html

response

<html>

  DIRS
  <a href="/public/house">house</a>
  <a href="/public/office">office</a>

  FILES
  <a href="/public/bed.jpg" target="_blank">bed.jpg</a>
  <a href="/public/kitchen.jpg" target="_blank">kitchen.jpg</a>
</html>

Remove the dependency on send?

I think the send module does not really fit our needs here, and we should look for a long term solution (see #46 for some hacky details). Basically the gist is that it does not implement the Streams 2/3 API, so we cannot pass it down to reply.send().

I will engage on the send repo to see if there is work we can do there, if we should fork it, or if we should lift some piece of code from there in here. See: pillarjs/send#159.

cc @diffcunha

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.