GithubHelp home page GithubHelp logo

fastify / fastify-express Goto Github PK

View Code? Open in Web Editor NEW
228.0 18.0 24.0 133 KB

Express compatibility layer for Fastify

License: MIT License

JavaScript 98.80% TypeScript 1.20%
fastify express compatibility fastify-plugin

fastify-express's Introduction

@fastify/express

CI NPM version js-standard-style

This plugin adds full Express compatibility to Fastify, it exposes the same use function of Express, and it allows you to use any Express middleware or application.

Note This plugin should not be used as a long-term solution, it aims to help you have a smooth transition from Express to Fastify, but you should migrate your Express specific code to Fastify over time.
Since Express does not support Node.js core HTTP/2 module, this plugin does not support HTTP/2 too.

Install

npm i @fastify/express

Usage

Register the plugin and start using your Express middlewares.

const Fastify = require('fastify')

async function build () {
  const fastify = Fastify()
  await fastify.register(require('@fastify/express'))
  // do you know we also have cors support?
  // https://github.com/fastify/fastify-cors
  fastify.use(require('cors')())
  // express.Application is also accessible
  fastify.express.disabled('x-powered-by') // true
  return fastify
}

build()
  .then(fastify => fastify.listen({ port: 3000 }))
  .catch(console.log)

Add a complete application

You can register an entire Express application and make it work with Fastify. Remember, @fastify/express is just express under the covers and requires the same body parsers as you'd use in express.

// index.js
const fastify = require('fastify')()
const express = require('express')
const router = express.Router()

router.use(function (req, res, next) {
  res.setHeader('x-custom', true)
  next()
})

router.get('/hello', (req, res) => {
  res.status(201)
  res.json({ hello: 'world' })
})

router.get('/foo', (req, res) => {
  res.status(400)
  res.json({ foo: 'bar' })
})

router.patch('/bar', (req, res) => {
  if (!req.body || Object.keys(req.body).length === 0) {
    res.status(400)
    res.json({ msg: 'no req.body'})
  } else {
    res.status(200)
    res.json(req.body)
  }
})

router.use('*', (req, res) => {
  res.status(404)
  res.json({ msg: 'not found'})
})

fastify.register(require('@fastify/express'))
  .after(() => {
    fastify.use(express.urlencoded({extended: false})) // for Postman x-www-form-urlencoded
    fastify.use(express.json())

    fastify.use(router)
  })

fastify.listen({ port: 3000 }, console.log)

Testing Your App

Run node index.js to start your server. Then run the following commands to ensure your server is working. Use the optional -v flag in curl for verbose output.

me@computer ~ % curl -X GET http://localhost:3000/hello
{"hello":"world"}%
me@computer ~ % curl -X GET http://localhost:3000/foo
{"foo":"bar"}%
me@computer ~ % curl -X GET http://localhost:3000/bar
{"msg":"not found"}%
me@computer ~ % curl -X PATCH -H 'content-type:application/json' http://localhost:3000/bar  
{"msg":"no req.body"}%
me@computer ~ % curl -X PATCH -H 'content-type:application/json' -d '{"foo2":"bar2"}' http://localhost:3000/bar
{"foo2":"bar2"}%  

Encapsulation support

The encapsulation works as usual with Fastify, you can register the plugin in a subsystem and your express code will work only inside there, or you can declare the express plugin top level and register a middleware in a nested plugin, and the middleware will be executed only for the nested routes of the specific plugin.

Register the plugin in its own subsystem:

const fastify = require('fastify')()

fastify.register(subsystem)

async function subsystem (fastify, opts) {
  await fastify.register(require('@fastify/express'))
  fastify.use(require('cors')())
}

Register a middleware in a specific plugin:

const fastify = require('fastify')()

fastify
  .register(require('@fastify/express'))
  .register(subsystem)

async function subsystem (fastify, opts) {
  fastify.use(require('cors')())
}

Hooks and middlewares

Every registered middleware will be run during the onRequest hook phase, so the registration order is important.
Take a look at the Lifecycle documentation page to understand better how every request is executed.

const fastify = require('fastify')()

fastify
  .register(require('@fastify/express'))
  .register(subsystem)

async function subsystem (fastify, opts) {
  fastify.addHook('onRequest', async (req, reply) => {
    console.log('first')
  })

  fastify.use((req, res, next) => {
    console.log('second')
    next()
  })

  fastify.addHook('onRequest', async (req, reply) => {
    console.log('third')
  })
}

Restrict middleware execution to a certain path(s)

If you need to run a middleware only under certain path(s), just pass the path as first parameter to use and you are done!

const fastify = require('fastify')()
const path = require('node:path')
const serveStatic = require('serve-static')

fastify
  .register(require('@fastify/express'))
  .register(subsystem)

async function subsystem (fastify, opts) {
  // Single path
  fastify.use('/css', serveStatic(path.join(__dirname, '/assets')))

  // Wildcard path
  fastify.use('/css/*', serveStatic(path.join(__dirname, '/assets')))

  // Multiple paths
  fastify.use(['/css', '/js'], serveStatic(path.join(__dirname, '/assets')))
}

Wrap Express req in Proxy

It is possible to wrap the Express request object in a Proxy by passing createProxyHandler function to generate the Proxy handler. The function will receive the Fastify request object as the first parameter.

For example using Proxy to expose something from Fastify request into the Express request.

fastify.decorateRequest('welcomeMessage', 'Hello World');
fastify.register(expressPlugin, {
  createProxyHandler: fastifyRequest => ({
    get (target, prop) {
      if (prop === 'welcomeMessage') {
        return fastifyRequest[prop]
      }

      return target[prop]
    }
  })
})

TypeScript support

To use this module with TypeScript, make sure to install @types/express.

You will need to add "types": ["@fastify/express"] to your tsconfig.json file when using require to import the plugin.

Middlewares alternatives

Fastify offers some alternatives to the most commonly used middlewares, following, you can find a list.

Express Middleware Fastify Plugin
helmet @fastify/helmet
cors @fastify/cors
serve-static @fastify/static

Troubleshooting

POST request with body hangs up

body-parser library incompatible with fastify-express, when you have fastify routes and any express middlewares. Any POST requests with body, which body-parser will try to parse, will be hangs up.

Example application:

const Fastify = require('fastify')
const Express = require('express')
const expressPlugin = require('@fastify/express')
const bodyParser = require('body-parser')

const fastify = Fastify()
const express = Express()

express.use(bodyParser.urlencoded({ extended: false }))

await fastify.register(expressPlugin)

fastify.use(express)

// this route will never reply
fastify.post('/hello', (req, reply) => {
  return { hello: 'world' }
})

For this case, you need to remove body-parser, install @fastify/formbody and change @fastify/express options:

const Fastify = require('fastify')
const Express = require('express')
const expressPlugin = require('@fastify/express')
const fastifyFormBody = require('@fastify/formbody')

const fastify = Fastify()
const express = Express()

await fastify.register(fastifyFormBody)
await fastify.register(expressPlugin, {
  // run express after `@fastify/formbody` logic
  expressHook: 'preHandler'
})

fastify.use(express)

// it works!
fastify.post('/hello', (req, reply) => {
  return { hello: 'world' }
})

License

Licensed under MIT.
express license

fastify-express's People

Contributors

10xlacroixdrinker avatar delvedor avatar dependabot[bot] avatar eomm avatar etto91 avatar everett1992 avatar fdawgs avatar flipperlite avatar github-actions[bot] avatar guillenotfound avatar is2ei avatar jsumners avatar mcollina avatar raon0211 avatar rluvaton avatar sachinahya avatar salmanm avatar simenb avatar superoleg39 avatar tastypackets avatar uzlopak avatar zekth 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

fastify-express's Issues

TypeScript declarations specify non-existent named export

Prerequisites

  • I have written a descriptive issue title
  • I have searched existing issues to ensure the bug has not already been reported

Fastify version

4.2.0

Plugin version

2.0.0

Node.js version

16.14.2

Operating system

macOS

Operating system version (i.e. 20.04, 11.3, 10)

12.4

Description

The types describe a named export of fastifyExpress but there is no such export. The plugin is only exported via module.exports.

As such, TS reports this as valid:

import { fastifyExpress } from '@fastify/express';

But it fails at runtime:

/Users/sachinahya/code/play/vite-react-18-ssr-boilerplate/node_modules/avvio/boot.js:207
    throw new AVV_ERR_PLUGIN_NOT_VALID(typeof plugin)
          ^
AvvioError [Error]: Plugin must be a function or a promise. Received: 'undefined'
    at assertPlugin (/Users/sachinahya/code/play/vite-react-18-ssr-boilerplate/node_modules/avvio/boot.js:207:11)
    at Boot._addPlugin (/Users/sachinahya/code/play/vite-react-18-ssr-boilerplate/node_modules/avvio/boot.js:240:12)
    at Boot.use (/Users/sachinahya/code/play/vite-react-18-ssr-boilerplate/node_modules/avvio/boot.js:216:25)
    at Object.server.<computed> [as register] (/Users/sachinahya/code/play/vite-react-18-ssr-boilerplate/node_modules/avvio/boot.js:40:14)
    at createServer (/Users/sachinahya/code/play/vite-react-18-ssr-boilerplate/src/server/server-dev.ts:20:13)
    at Object.<anonymous> (/Users/sachinahya/code/play/vite-react-18-ssr-boilerplate/src/server/server-dev.ts:103:6)
    at Module._compile (node:internal/modules/cjs/loader:1103:14)
    at Module.m._compile (/Users/sachinahya/code/play/vite-react-18-ssr-boilerplate/node_modules/ts-node/src/index.ts:1597:23)
    at Module._extensions..js (node:internal/modules/cjs/loader:1157:10)
    at Object.require.extensions.<computed> [as .ts] (/Users/sachinahya/code/play/vite-react-18-ssr-boilerplate/node_modules/ts-node/src/index.ts:1600:12) {
  code: 'AVV_ERR_PLUGIN_NOT_VALID'
}

Steps to Reproduce

Register the plugin using a named export and it produces the error above.

import { fastifyExpress } from '@fastify/express';

const app = fastify();
await app.register(fastifyExpress);

Expected Behavior

TypeScript should report an error specifying that @fastify/express does not export fastifyExpress.

When testing express application with inject, payload is always empty

Prerequisites

  • I have written a descriptive issue title
  • I have searched existing issues to ensure the bug has not already been reported

Fastify version

3.22.1

Plugin version

0.3.3

Node.js version

16.4.0

Operating system

macOS

Operating system version (i.e. 20.04, 11.3, 10)

11.2.1

Description

I'm having an express application which will transition completely to Fastify in the future. We want to write integration tests for this application and leverage fastify.inject but when executing a request, the payload is always empty.

To run our tests we are using Jest (27.2.5).

Steps to Reproduce

// server.ts

const router = express.Router();

const app = fastify();

router.get('/test', (req, res) => {
  res.status(201);
  res.json({ hello: 'world' });
});

await app.register(fastifyExpress).after(async () => {
  app.use(router);
});
// test.ts

test('No query parameters', async () => {
  app.inject().get('/test').end((err, response) => {
    expect(response.statusCode).toEqual(201);
    expect(response.json()).toEqual({ hello: 'world' });
  });
});

Expected Behavior

I would expect a body and not an empty string.

Express routes don't work when an Express router is added inside a plugin

๐Ÿ› Bug Report

This is probably less of a bug and more of a potential opportunity to improve the documentation for this plugin. I would like to clarify a couple of things before I open a pull request.

The README for this plugin provides an example of how you can add a complete application (an Express router instance in the example). If I follow this example and use() an Express router instance at the root context of a Fastify server the Express routes work as expected. If I use() the Express router instance inside a plugin, the routes do not work.

This was surprising as the encapsulation support example implies that all Express code can be registered inside a plugin, although it doesn't explicitly include Express routes.

This issue has been discussed on the Fastify Discord. @etino has demonstrated that it is possible to call use() with an Express router instance inside of a plugin and have the routes work if you wrap the plugin with fastify-plugin. This hints at this issue being related to scope.

In conclusion, this might be a bug, but my understanding of scoping in Fastify is not complete enough to determine if this is expected behaviour. If it is expected behaviour, I propose updating the README in one of two ways:

  1. Document that if you want to add Express routes inside of a plugin that you will need to wrap that plugin with fastify-plugin (unless that is not advisable for reasons I'm unaware of).
  2. Document that Express routes must be added by calling use() on the root context of a Fastify server.

Sidenote: I've noticed a test for registering a complete Express application i.e. an Express server instance, as opposed to an Express router instance. Should this be documented in the README?

To Reproduce

Steps to reproduce the behavior:

import express from "express";
import Fastify from "fastify";
import FastifyExpress from "fastify-express";

const fastify = Fastify({
	logger: true,
});

fastify.register(async function routes(fastify) {

	// 200 OK
	fastify.get("/fastify", (request, reply) => {
		reply.send({ hello: "fastify" })
	});

	await fastify.register(FastifyExpress);

	const router = express.Router();

	// Error: 404 Not Found
	router.get("/express", (request, response) => {
		response.json({ hello: "express" });
	});

	fastify.use(router);
});

try {
	await fastify.listen(3000);
} catch (error) {
	fastify.log.error(error);
	process.exit(1);
}

Expected behavior

Express routes work regardless of the scope at which they are added with the use() method that this plugin exposes. If this is not desirable, then the documentation should reflect that.

Your Environment

  • node version: 14.16.0
  • fastify version: 3.13.0
  • os: Linux

Document that HTTP/2 is not supported when using fastify-express

Prerequisites

  • I have written a descriptive issue title
  • I have searched existing issues to ensure the bug has not already been reported

Fastify version

3.22.1

Plugin version

No response

Node.js version

14.17.3

Operating system

macOS

Operating system version (i.e. 20.04, 11.3, 10)

11.5.2

Description

I'm trying to setup a HTTP2 server but the connections get stuck, never returning anything. It does even reach the request handler code.

Steps to Reproduce

import Fastify from 'fastify';
import expressPlugin from 'fastify-express';
import cors from 'cors';

const PORT = process.env.PORT ?? 3000;


async function build() {
  const fastify = Fastify({ http2: true });

  await fastify.register(expressPlugin);
  fastify.use(cors());

  fastify.get('/health', function (_, reply) {
    console.log('d');
    reply.send({ status: 'health' });
  });
  
  return fastify;
}

void (async function main() {
  try {
    const fastify = await build();
    console.log('Server listening on port ' + PORT);
    await fastify.listen(PORT, '0.0.0.0');
  } catch (exception) {
    console.log(exception);
  }
})();

Now I make the request with the command suggested on the documentation:

npx h2url http://0.0.0.0:3000/health

And it gets stuck! The same happens if I use curl:

curl --http2-prior-knowledge --insecure --verbose http://0.0.0.0:3000/health 

# output
*   Trying 0.0.0.0...
* TCP_NODELAY set
* Connected to 0.0.0.0 (127.0.0.1) port 3000 (#0)
* Using HTTP2, server supports multi-use
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* Using Stream ID: 1 (easy handle 0x7fce8a80f800)
> GET /health HTTP/2
> Host: 0.0.0.0:3000
> User-Agent: curl/7.64.1
> Accept: */*
> 
* Connection state changed (MAX_CONCURRENT_STREAMS == 4294967295)!

As you can see the connection is made but no response is sent. I have added a console.log statement to the /health path but it's never shown, so that code is never reached.

Expected Behavior

It should return the json specified in the response.

If an express router handles a request, `onSend` hooks are not called

๐Ÿ› Bug Report

We're trying to migrate from express-session to fastify-session. It mostly seems to be working except for the cookie-setting (which is not happening), meaning our session only lives as long as the request.

I've narrowed the issue down (I think) to fastify-session using onSend to set the cookie: https://github.com/SerayaEryn/fastify-session/blob/e201f78fc9d7bd33c6f2e84988be7c8af4b5a8a3/lib/fastifySession.js#L25

And I believe the issue is that we have a express.Router handling the request from the client that ends up setting a session, and then doing a redirect. Since an express handler handles the request, I believe onSend is not called, and no Set-Cookie header is provided in the response.

To Reproduce

Steps to reproduce the behavior:

const Fastify = require('fastify');
const { Router } = require('express');

async function build() {
  const fastify = Fastify();
  fastify.addHook('onSend', (req, res, body, next) => {
    console.log('on send called!');
    next();
  });

  await fastify.register(require('fastify-express'));

  const router = Router();

  router.get('/expr', (_, res) => {
    res.json({ hello: 'world' });
  });

  fastify.use(router);

  fastify.get('/fast', () => {
    return { hello: 'fast' };
  });

  return fastify;
}

build()
  .then((fastify) => fastify.listen(4000))
  .catch(console.log);

If you do curl http://localhost:4000/fast you'll see a on send called! logged out, but doing curl http://localhost:4000/expr will not print that

Expected behavior

I'd expect onSend to still be called even though an express router has handled the request.

While this code (mixing express and fastify) is temporary, I'd really like to be able to land migration from express-session to fastify-session without also having to migrate all our handlers at the same time.

In theory we can probably try to port express-session's hacky res.end replacement, but that feels super brittle... Although I guess the contract of "all express handlers and middleware run in onRequest phase - if request is handled no further hooks are called". If that's the case, some help coming up with a workaround would be lovely. (can be hacky as longs as it's not too brittle - just need to be able to land pieces of migration rather than all at once)

Your Environment

  • node version: 14.15.3
  • fastify version: 3.12.0
  • os: Mac

Why the `expressHook` should be `preHandler` and not `preValidation`?

Prerequisites

  • I have written a descriptive issue title
  • I have searched existing issues to ensure the issue has not already been raised

Issue

In the docs there is this example in the Troubleshooting - POST request with body hangs up:

const Fastify = require('fastify')
const Express = require('express')
const expressPlugin = require('@fastify/express')
const fastifyFormBody = require('@fastify/formbody')

const fastify = Fastify()
const express = Express()

await fastify.register(fastifyFormBody)
await fastify.register(expressPlugin, {
  // run express after `@fastify/formbody` logic
  expressHook: 'preHandler'
})

fastify.use(express)

// it works!
fastify.post('/hello', (req, reply) => {
  return { hello: 'world' }
})

Why the expressHook should be preHandler and not preValidation?

if it's preHandler, middlewares that want to always run won't (for example - logger [we are planning to migrate to pino :)])

and preValidation is the first hook that have the body parsed, no?

Headers set by fastify are ignored if `express` handles request

๐Ÿ› Bug Report

If I set headers via some hook in fastify, they are not included in the response if an express handler handles the request.

This is not true the other way around - if an express middleware sets a header it's included in the response no matter of it's express or fastify the responds.

To Reproduce

Steps to reproduce the behavior:

const fastify = require('fastify')();
const fastifyExpress = require('fastify-express');
const fp = require('fastify-plugin');
const express = require('express');

fastify.register(
  fp(
    (instance, _, next) => {
      instance.addHook('preValidation', (_, reply, done) => {
        reply.headers({
          foo: 'bar',
        });

        done();
      });

      next();
    },
    { fastify: '3.x' },
  ),
);

fastify.register(fastifyExpress).after(() => {
  const router = express.Router();

  router.use('/express', (_, res) => {
    res.sendStatus(200);
  });

  fastify.use(router);
});

fastify.get('/fastify', (_, reply) => {
  reply.code(200).send();
});

fastify.listen(3000);
$ curl -i http://localhost:3000/fastify 
HTTP/1.1 200 OK
foo: bar
content-length: 0
Date: Thu, 22 Oct 2020 10:48:01 GMT
Connection: keep-alive
Keep-Alive: timeout=5

$ curl -i http://localhost:3000/express
HTTP/1.1 200 OK
Content-Type: text/plain; charset=utf-8
Content-Length: 2
ETag: W/"2-nOO9QiTIwXgNtWtBJezz8kv3SLc"
Date: Thu, 22 Oct 2020 10:48:03 GMT
Connection: keep-alive
Keep-Alive: timeout=5

FWIW, using reply.raw.setHeader doesn't work either.

Expected behavior

I would expect the header to be written to the response regardless of express handling the request or not.

This makes it impossible to migrate parts of our app piecemeal to Fastify as the headers set (specifically HSTS headers now) are ignored.

Your Environment

  • node version: v12.19.0
  • fastify version: 3.7.0
  • os: Mac
  • any other relevant information

@fastify-express not working inside a plugin

Prerequisites

  • I have written a descriptive issue title
  • I have searched existing issues to ensure the bug has not already been reported

Fastify version

4.x.x

Plugin version

2.3.0

Node.js version

16.18.1

Operating system

macOS

Operating system version (i.e. 20.04, 11.3, 10)

Ventura 13.0

Description

Every express middleware (regardless of being an app, router, middleware, or route) mounted inside a fastify plugin isn't working and returns 404. I've added encapsulation tests to mimic the application.test.js file, and the only test passing is: Should not expose the express app on the root fastify instance when registered inside a plugin.

To make this clearer, this is not working:

fastify.register(expressPlugin).after(function () {
    fastify.register(function (instance, opts, done) {
      const express = Express()
      express.use(function (req, res, next) {
        res.setHeader('x-custom', true)
        next()
      })

      express.get('/hello', (req, res) => {
        res.status(201)
        res.json({ hello: 'world' })
      })
      instance.use(express)
      done()
    })
  })

Steps to Reproduce

Clone https://github.com/fox1t/fastify-express and run npm i && npm run test:unit ./test/encapsulation.test.js

Expected Behavior

As far as the docs say, it should be possible to add express apps, routers, middleware, and routes inside a plugin. Ref: https://github.com/fox1t/fastify-express#encapsulation-support

Swagger MIME type ('application/json') is not a supported stylesheet MIME type

Prerequisites

  • I have written a descriptive issue title
  • I have searched existing issues to ensure the issue has not already been raised

Issue

Node.js version

14.21.2

Npm version

6.14.17

Fastify version

4.24.3

Description

I am using @fastify/express 2.3.0 and @fastify/http-proxy 9.3.0 and swagger-ui-express 4.3.0 and swagger-jsdoc 6.1.0. Trying to configure swagger, but css and js link is not working. .

Configuration

const express = require('express');
const router = express.Router();
const swaggerUI = require('swagger-ui-express');
const swaggerDocument = require('./public/uploads/swagger.json');
const serveStatic = require('serve-static');

async function startOrigin() {
  const server = Fastify({
    logger: false,
  })
  
  server.register(require('@fastify/express')).after(() => {
    server.use(express.urlencoded({ extended: true })); // for Postman x-www-form-urlencoded
    server.use(express.json());
    server.use(serveStatic(path.join(__dirname, '/public')));
    server.use('/api', router);

  });
  router.get('/', swaggerUI.serve, swaggerUI.setup(swaggerDocument));

  await server.listen();
  return server;
}
async function startProxy(upstream) {
  const proxyServer = Fastify({
    logger: false,
  });
  proxyServer.register(require('@fastify/multipart'));
  proxyServer.register(proxy, {
    upstream,
    undici: true,
    replyOptions: {
      rewriteRequestHeaders: (req, headers) => {
        return {
          ...headers,
          authorization: 'Bearer token' 
        };
      }
    }
  });
  await proxyServer.listen({ port: process.env.PORT || '3000' });
  return proxyServer;
}

async function run() {
  const origin = await startOrigin();

  const upstream = `http://localhost:${origin.server.address().port}`;

  console.log('origin started', upstream);

  const proxy = await startProxy(upstream);

  console.log('proxy started', `http://localhost:${proxy.server.address().port}`);
}

run();

am I doing wrong something. Please check this.

Response

image

Thanks in advance

Headers are `null` when using `.inject`

Prerequisites

  • I have written a descriptive issue title
  • I have searched existing issues to ensure it has not already been reported

Fastify version

3.20.2

Plugin version

0.3.3

Node.js version

14.17.5

Operating system

macOS

Operating system version (i.e. 20.04, 11.3, 10)

11.5.1

Description

When using fastify.inject and this plugin, res.headers is always null. The headers are in res.raw.res.getHeaders() FWIW.

Debugging, https://github.com/fastify/light-my-request/blob/014c1c8016f8605b5ca4c7906f527d5ab0b17267/lib/response.js#L66-L81 is never called

Steps to Reproduce

In this repo, change fastify.listen to fastify.inject

diff --git i/test/application.test.js w/test/application.test.js
index a8e1915..a28e4dd 100644
--- i/test/application.test.js
+++ w/test/application.test.js
@@ -166,14 +166,11 @@ test('Should flush headers if express handles request', t => {
     .register(expressPlugin)
     .after(() => { fastify.use(router) })
 
-  fastify.listen(0, (err, address) => {
+  fastify.inject({
+    method: 'GET',
+    url: '/'
+  }, (err, res) => {
     t.error(err)
-    sget({
-      method: 'GET',
-      url: address
-    }, (err, res) => {
-      t.error(err)
-      t.strictEqual(res.headers.foo, 'bar')
-    })
+    t.strictEqual(res.headers.foo, 'bar')
   })
 })

image

Expected Behavior

headers should be populated

Unable to get plugin working with typescript + require

Prerequisites

  • I have written a descriptive issue title
  • I have searched existing issues to ensure the issue has not already been raised

Issue

If I use require to import the plugin that won't play well with Typescript and will raise the following error:

TSError: โจฏ Unable to compile TypeScript:
src/node/loaders/vite.ts:21:7 - error TS2339: Property 'use' does not exist on type 'FastifyInstance<Server, IncomingMessage, ServerResponse, FastifyLoggerInstance>'.

21   app.use(viteServer.middlewares)

Instead if I import the plugin using import it works well, but I don't need the plugin on production, just on development, so is there any way to get it working using require instead of import?

Express middleware cannot access request.body

Prerequisites

  • I have written a descriptive issue title
  • I have searched existing issues to ensure the bug has not already been reported

Fastify version

3.27.4

Plugin version

0.3.3

Node.js version

16.13.0

Operating system

macOS

Operating system version (i.e. 20.04, 11.3, 10)

Monterery 12.2.1

Description

Express middleware cannot access request.body for PATCH, POST routes. The request.body is null when the addHook('onRequest', enhanceRequest) is called in index.js line 39. I'm coding in typescript 4.6.2.

Steps to Reproduce

  1. Copy the following code into index.ts and package.json.
  2. Run npm i --save fastify fastify-express express && npm i -D typescript @types/node @types/express && npx tsc --init
  3. Run npm run build
  4. Run npm start
  5. Run curl -X PATCH -H 'content-type:application/json' --data-raw '{"executor":"NAME"}' http://localhost:4445/
// index.ts
import { NextFunction, Request, Response, Router } from 'express'
import Fastify, { FastifyInstance } from 'fastify'
import { fastifyExpress } from 'fastify-express'

async function build() {
    const fastify = Fastify()

    // https://github.com/fastify/fastify-express
    await fastify.register(fastifyExpress)
    fastify.use((req, res, next) => {
        if (req.method !== 'GET' && !req.body) throw new Error('Passing in body will still raise error.')
        return next()
    })
    const router = Router()
    router.get('*', (req: Request, res: Response, next: NextFunction) => {
        return next(req.method)
    })
    router.patch('*', (req: Request, res: Response, next: NextFunction) => {
        return next(req.method)
    })
    router.post('*', (req: Request, res: Response, next: NextFunction) => {
        return next(req.method)
    })
    router.delete('*', (req: Request, res: Response, next: NextFunction) => {
        return next(req.method)
    })
    router.head('*', (req: Request, res: Response, next: NextFunction) => {
        return next(req.method)
    })
    fastify.use(router)

    return fastify
}

const serve = async (fastify: FastifyInstance) => {
    try {
        const port = 4445
        await fastify.listen(port)
        console.info(`Server listening to PORT ${port}`)
    } catch (err) {
        console.error(err)
        process.exit()
    }
}

build()
    .then(serve)
{
  "name": "test",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "build": "tsc -p tsconfig.json",
    "start": "node index.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "express": "^4.17.3",
    "fastify": "^3.27.4",
    "fastify-express": "^0.3.3"
  },
  "devDependencies": {
    "@types/express": "^4.17.13",
    "@types/node": "^17.0.21",
    "typescript": "^4.6.2"
  }
}

Expected Behavior

The request.body should be populated with the JSON body being sent in. I've tested in both curl and Postman.

me@mba2020 ~ % curl -X PATCH -H 'content-type:application/json' --data-raw '{"executor":"NAME"}' http://localhost:4445/             
{"statusCode":500,"error":"Internal Server Error","message":"Passing in body will still raise error."}

`fastify` should be added as a dep to `package.json`

Prerequisites

  • I have written a descriptive issue title
  • I have searched existing issues to ensure it has not already been reported

Fastify version

3

Plugin version

1

Node.js version

16

Operating system

macOS

Operating system version (i.e. 20.04, 11.3, 10)

BigSur

Description

This breaks with yarn 2 and pnpm projects that expect all referenced deps to be in the package.json.

Error: Cannot find module 'fastify/lib/symbols'
Require stack:
- /xxx/node_modules/.pnpm/[email protected]/node_modules/fastify-express/index.js

In npm and yarn, fastify-plugin probably hoists fastify so it works.

Steps to Reproduce

  • Create pnpm project.
  • Add express-fastify.
  • Set hoist: false in .npmrc.

Expected Behavior

No error.

Express wildcard routes override Fastify routes

Prerequisites

  • I have written a descriptive issue title
  • I have searched existing issues to ensure the bug has not already been reported

Fastify version

4.19.2

Plugin version

2.3.0

Node.js version

18.5.0

Operating system

macOS

Operating system version (i.e. 20.04, 11.3, 10)

Ventura 13.4

Description

When mixing a Fastify route and an Express route with a wildcard (*), the handler for the Express wildcard route will always be called over the handler for the Fastify route, even if the Fastify route is configured first.

Steps to Reproduce

const Fastify = require('fastify')
const express = require('express')
const router = express.Router()

async function build () {
    const fastify = Fastify()
    fastify.get('/hello-fastify', function (request, reply) {
        reply.send({
            hello: 'fastify'
        })
    })
    await fastify.register(require('@fastify/express'))
    router.get('/hello-express', (req, res) => {
        res.status(201)
        res.json({ hello: 'express' })
    })
    router.get('*', (req, res) => {
        res.status(404)
        res.json({ msg: 'not found'})
    })
    fastify.use(router);
    return fastify
}

build()
    .then(fastify => fastify.listen({ port: 3000 }))
    .catch(console.log)
curl -X GET http://localhost:3000/hello-express    
{"hello":"express"}
curl -X GET http://localhost:3000/hello-fastify   
{"msg":"not found"}

Expected Behavior

When sending a GET request to the /hello-fastify endpoint

curl -X GET http://localhost:3000/hello-fastify  

the handler function provided when calling fastify.get('/hello-fastify, handler function() {...}) should be called and the response should be

{ hello: 'fastify' }

.

FastifyError when using fastify-express with Nest.js and Fastify

Prerequisites

  • I have written a descriptive issue title
  • I have searched existing issues to ensure the bug has not already been reported

Fastify version

3.24.0

Plugin version

0.3.3

Node.js version

14.17.6

Operating system

macOS

Operating system version (i.e. 20.04, 11.3, 10)

11.6

Description

When I run npm run start on minimal example with nest.js and fastify-express, I have following:

antonmalmygin@Antons-MBP project-name % npm run start

> [email protected] start
> nest start

[Nest] 10969  - 11/30/2021, 12:59:26 PM     LOG [NestFactory] Starting Nest application...
[Nest] 10969  - 11/30/2021, 12:59:26 PM     LOG [InstanceLoader] AppModule dependencies initialized +18ms
(node:10969) UnhandledPromiseRejectionWarning: FastifyError: The decorator 'use' has already been added!
    at decorate (/Users/antonmalmygin/Desktop/Dev/another/nest-example/project-name/node_modules/fastify/lib/decorate.js:22:11)
    at Object.decorateFastify [as decorate] (/Users/antonmalmygin/Desktop/Dev/another/nest-example/project-name/node_modules/fastify/lib/decorate.js:65:3)
    at expressPlugin (/Users/antonmalmygin/Desktop/Dev/another/nest-example/project-name/node_modules/fastify-express/index.js:9:11)
    at Plugin.exec (/Users/antonmalmygin/Desktop/Dev/another/nest-example/project-name/node_modules/avvio/plugin.js:132:19)
    at Boot.loadPlugin (/Users/antonmalmygin/Desktop/Dev/another/nest-example/project-name/node_modules/avvio/plugin.js:267:10)
    at release (/Users/antonmalmygin/Desktop/Dev/another/nest-example/project-name/node_modules/fastq/queue.js:149:16)
    at Object.resume (/Users/antonmalmygin/Desktop/Dev/another/nest-example/project-name/node_modules/fastq/queue.js:82:7)
    at setup (/Users/antonmalmygin/Desktop/Dev/another/nest-example/project-name/node_modules/avvio/plugin.js:164:12)
    at Plugin.loadedSoFar (/Users/antonmalmygin/Desktop/Dev/another/nest-example/project-name/node_modules/avvio/plugin.js:176:7)
    at Boot._loadRegistered (/Users/antonmalmygin/Desktop/Dev/another/nest-example/project-name/node_modules/avvio/boot.js:227:17)
(Use `node --trace-warnings ...` to show where the warning was created)
(node:10969) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 1)
(node:10969) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
(node:10969) UnhandledPromiseRejectionWarning: Error: ERR_AVVIO_PLUGIN_TIMEOUT: plugin did not start in time: /Users/antonmalmygin/Desktop/Dev/another/nest-example/project-name/node_modules/fastify-express/index.js. You may have forgotten to call 'done' function or to resolve a Promise
    at Timeout._onTimeout (/Users/antonmalmygin/Desktop/Dev/another/nest-example/project-name/node_modules/avvio/plugin.js:123:19)
    at listOnTimeout (internal/timers.js:557:17)
    at processTimers (internal/timers.js:500:7)
(node:10969) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 2)

I saw similar issue being reported here #36, but changing Fastify version doesn't solved the issue for me (and also I use Nest.js).

Steps to Reproduce

I created a minimal example to reproduce here https://github.com/amOgury/nestjs-fastify-express-example

It's identical to Nest.js example with Fastify from here https://docs.nestjs.com/techniques/performance, except that I register fastify-express here https://github.com/amOgury/nestjs-fastify-express-example/blob/main/src/main.ts#L13

Expected Behavior

Minimal example should start normally

FST_ERR_DEC_ALREADY_PRESENT when register fastify-express

Prerequisites

  • I have written a descriptive issue title
  • I have searched existing issues to ensure it has not already been reported

Fastify version

3.14.0

Plugin version

0.3.2

Node.js version

12.13.0

Operating system

macOS

Operating system version (i.e. 20.04, 11.3, 10)

10.13.6

Description

FST_ERR_DEC_ALREADY_PRESENT when register fastify-express

image

Steps to Reproduce

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

async function build() {
    const fastifyInstance = fastify();
    await fastifyInstance.register(require('fastify-express'));
    fastifyInstance.use(cors());
    return fastifyInstance;
};

build().then(fastify => fastify.listen(3000, (err, port) => {
    console.warn(`server listening on ${port}`);
    if (err) {
        process.exit(1);
    };
})).catch(console.log);

Expected Behavior

No response

`request.url` is mutated between `onRequest` and `onResponse`

๐Ÿ› Bug Report

request.url is different in onRequest vs onResponse.

To Reproduce

Steps to reproduce the behavior:

const fastify = require('fastify')();
const fastifyExpress = require('fastify-express');
const fp = require('fastify-plugin');
const express = require('express');

fastify.register(
  fp(
    (instance, _, next) => {
      instance.addHook('onRequest', (request, _, next) => {
        console.log('req!', request.url);

        next();
      });

      instance.addHook('onResponse', (request, reply, next) => {
        console.log('res!', request.url);

        next();
      });

      next();
    },
    { fastify: '3.x' },
  ),
);

fastify.register(fastifyExpress).after(() => {
  const mainRouter = express.Router();

  const innerRouter = express.Router();

  mainRouter.use('/hubba', innerRouter);

  innerRouter.get('/bubba', (req, res) => {
    console.log('handler', req.url, req.originalUrl);

    res.sendStatus(200);
  });

  fastify.use(mainRouter);
});

fastify.listen(3000);
$ curl http://localhost:3000/hubba/bubba

This will log the following

req! /hubba/bubba
handler /bubba /hubba/bubba
res! /bubba

Expected behavior

I'd expect the logged output to be

req! /hubba/bubba
handler /bubba /hubba/bubba
res! /hubba/bubba

If not that, at least access to the original URL. It's on req.raw.originalUrl due to fastify-express putting it there during an onRequest, but that shouldn't be necessary.

Your Environment

  • node version: 12.19.0
  • fastify version: 3.6.0
  • os: Mac
  • any other relevant information

ERR_AVVIO_PLUGIN_TIMEOUT using Add a complete application example

๐Ÿ› Bug Report

I'm trying to add parse-server to a fastify server and i got this error ERR_AVVIO_PLUGIN_TIMEOUT.
I have tried to copy and paste the example in the readme and i got the same error.

To Reproduce

Steps to reproduce the behavior:

const fastify = require('fastify')();
const router = require('express').Router();
router.use(function (req, res, next) {
  res.setHeader('x-custom', true);
  next();
});

router.get('/hello', (req, res) => {
  res.status(201);
  res.json({ hello: 'world' });
});

router.get('/foo', (req, res) => {
  res.status(400);
  res.json({ foo: 'bar' });
});

fastify
  .register(require('fastify-express'))
  .after(() => fastify.use(router));

fastify.listen(3000, console.log);

ERROR

Error: ERR_AVVIO_PLUGIN_TIMEOUT: plugin did not start in time: bound _after
    at Timeout._onTimeout (/*/node_modules/avvio/plugin.js:120:19)
    at listOnTimeout (internal/timers.js:549:17)
    at processTimers (internal/timers.js:492:7) {
  code: 'ERR_AVVIO_PLUGIN_TIMEOUT',
  fn: [Function: bound _after]
}

Your Environment

  • node version: 13.8.0
  • fastify version: 3.0.0
  • os: Mac
  • fastify-express version: 0.1.0

Issue v5 release

The next branch should be ready for issuing a release to coincide with fastify@5.

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.