Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for class based plugins #1673

Closed

Conversation

SerayaEryn
Copy link
Contributor

I started looking into a possible implementation of class based plugins (#1655).

There are decorators for each http method: @Get, @Post, @Put, @Delete, @Put, @Head, @Patch and @Options. In addition to that there is a @All decorator that adds the handler for all http methods. They accept the parameters like the methods on the fastify instance: @Method(path, [options])
Furthermore there is a @Hook(hook) decorator that allows to add hooks.

These decorators for the http methods and the hook decorator seem to work well.

I also have build decorators, that allow to decorate the fastify instance, request and reply.
However I don't know whether this solution is nice.
It is not possible to check whether the fastify instance, request or reply is already decorated.
And since a function can't access the this from the plugin and the request (or reply) the decorated method needs to return another function.

class MyPlugin {
  @DecorateRequest('utility')
  addUtility () {
    return function () {
      // something very useful
    }
  }
}

Maybe the following approach might be better:

class MyPlugin {
  @SupplyWithInstance // or something similar
  decorate (instance) {
    if(!instance.hasRequestDecorator('utility')) {
      instance.decorateRequest('utility', function () {
        // something very useful
      })
    }
  }
}

The documentation for the class based plugins is not finished yet.

I think this feature should be marked as experimental as long as the typescript feature is experimental and the proposal is not finalized. And maybe there should be an option that needs to be enabled?

I also noticed that it might be nice to add some kind of @Plugin(metadata) decorator to fastify-plugin, that allows to add metadata for a plugin.

Checklist

  • run npm run test and npm run benchmark
  • tests and/or benchmarks are included
  • documentation is changed or added
  • commit message and code follows Code of conduct

Copy link
Member

@mcollina mcollina left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks amazing, good work! I've added a few notes.

'use strict'

const { kPluginMetadata } = require('./symbols')
require('reflect-metadata')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm -1 on adding any dependency that modifies global state. Can we do this without this library or with a work-around?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could store it at the plugin using a symbol.

"target": "es6",
"module": "commonjs",
"strict": false,
"experimentalDecorators": true
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I've heard they are going to change this in the next version of TS. When will decorators be out of experimental? Have you got any info?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, I got no information on this.
However I think we will know in two days: microsoft/TypeScript#30555

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Decorators are still experimental in typescript 3.5.x.

docs/ClassBasedPlugins.md Show resolved Hide resolved
docs/ClassBasedPlugins.md Show resolved Hide resolved
import { DecorateInstance } from 'fastify'

class MyPlugin {
@DecorateInstance()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We would need to add a decorator on the class itself to enable/disable encapsulation, i.e. setting https://github.com/fastify/fastify-plugin/blob/966248ce75eebe58727aaa5c676d410b4641d236/index.js#L16

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree. Shall we add this decorator in fastify-plugin? It could provide the other features of fastify-plugin as a decorator, too.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I opened a PR (fastify/fastify-plugin#73) in the fastify-plugin repository that adds such a decorator.

@Ethan-Arrowood
Copy link
Member

Are we planning on landing this in v2?

@mcollina
Copy link
Member

mcollina commented May 28, 2019 via email

package.json Outdated Show resolved Hide resolved
@Ethan-Arrowood
Copy link
Member

Per @mcollina confirmation, please rebase onto next branch, and similarly, change the PR base is to next instead of master. I recently did this for the typescript refactor PR so let me know if you have issues/questions 😁👍

@SerayaEryn
Copy link
Contributor Author

Ok, I'll rebase the branch and change the PR.

@SerayaEryn SerayaEryn force-pushed the add-support-for-class-based-plugins branch from 5546fc7 to 3c13d7b Compare May 31, 2019 18:32
@SerayaEryn SerayaEryn changed the base branch from master to next May 31, 2019 18:33

[kSetInstance] (instance) {
this.instance = instance
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don’t think this class is needed at all. We can just set the instance manually on the object, without using an accessor.

@Get('/')
async handler (request, response) {
t.ok(this.instance)
t.ok(this['instance'])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why this change?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That wasn't planed to stay. I fixed it.

module.exports.Hook = Hook
module.exports.DecorateRequest = DecorateRequest
module.exports.DecorateReply = DecorateReply
module.exports.DecorateInstance = DecorateInstance
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be possible to use these with JavaScript as well?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The decorators can be used with babel.
Using them without typescript or babel should be possible, but it is not very practical.

@mcollina
Copy link
Member

I really like this proposal. I'm a bit scared about how this would affect our LTS support, considering that TS decorators are still experimental.

Should we land them as experimental here, maybe logging a warning?

@SerayaEryn
Copy link
Contributor Author

I agree that they should be experimental and a warnung is a good idea.
I'll add a warning.

@Ethan-Arrowood
Copy link
Member

If this is to land in next I'd like it to be experimental until decorators are standard.

Additionally, when #1569 lands I believe some updates will need to be made with this proposal.

@stale
Copy link

stale bot commented Jul 3, 2019

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

@stale stale bot added the stale Issue or pr with more than 15 days of inactivity. label Jul 3, 2019
@SerayaEryn
Copy link
Contributor Author

Just a note: I am waiting until #1569 is being merged before I add typings for the new Decorators that this PR adds.

@stale stale bot removed the stale Issue or pr with more than 15 days of inactivity. label Jul 4, 2019
@stale
Copy link

stale bot commented Jul 19, 2019

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

@stale stale bot added the stale Issue or pr with more than 15 days of inactivity. label Jul 19, 2019
@stale stale bot closed this Jul 26, 2019
@jsumners jsumners added discussion Issues or PRs with this label will never stale and removed stale Issue or pr with more than 15 days of inactivity. labels Jul 26, 2019
@jsumners jsumners reopened this Jul 26, 2019
@SerayaEryn SerayaEryn force-pushed the add-support-for-class-based-plugins branch from 14b637d to 9527327 Compare August 29, 2019 19:03
@SerayaEryn
Copy link
Contributor Author

I rebased the branch since #1569 has been merged and added typings for the decorators.

@SerayaEryn SerayaEryn marked this pull request as ready for review August 29, 2019 19:08
@SerayaEryn
Copy link
Contributor Author

The test failures seem to be related to typescript@3.6.2. With typescript@3.5.3 all tests are passing.

@RafaelGSS
Copy link
Member

Hi @SerayaEryn! Really excellent addition.
But I have some doubts, based on this example retired of doc:

class MyPlugin {
  constructor (options) {
    // ...
  }
  @Get('/')
  async route (request, reply) {
    return 'hello world'
  }
}

@SerayaEryn
Copy link
Contributor Author

The decorators for the http methods have a second parameter that allows to add additional route options:

class MyPlugin {
  constructor (options) {
    // ...
  }
  @Get('/', { preHandler: yourPreHandler })
  async route (request, reply) {
    return 'hello world'
  }
}

@mcollina
Copy link
Member

mcollina commented Sep 1, 2019

This work is impressive.

CI is failing, would you mind taking a look?

@SerayaEryn
Copy link
Contributor Author

The new typescript version 3.6.2 seems to cause the failing tests. I got the same failing tests with that version on my local machine. With the older 3.5.3 version they pass.

I take a closer look next week.

@StarpTech
Copy link
Member

Great PR but I don't like that code is shipped to the core and only typescript is supported.

@StarpTech
Copy link
Member

Can we provide them as a plugin?

@SerayaEryn
Copy link
Contributor Author

Can we provide them as a plugin?

I think that should be possible.

@mcollina
Copy link
Member

mcollina commented Sep 20, 2019 via email

@SerayaEryn
Copy link
Contributor Author

I'll close this PR and will start working on a module as soon as i can.

@SerayaEryn SerayaEryn closed this Sep 28, 2019
@github-actions
Copy link

This pull request has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Feb 10, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
discussion Issues or PRs with this label will never stale
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants