X Tutup
The Wayback Machine - https://web.archive.org/web/20201028104849/https://github.com/feathersjs/feathers/issues/1976
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

Custom methods the Feathers way #1976

Open
daffl opened this issue Jun 2, 2020 · 6 comments
Open

Custom methods the Feathers way #1976

daffl opened this issue Jun 2, 2020 · 6 comments
Labels
Milestone

Comments

@daffl
Copy link
Member

@daffl daffl commented Jun 2, 2020

Custom methods are one of the most commonly requested features for Feathers. As the FAQ points out, it is possible to handle 90% of all use cases with the normal CRUD service interface and hooks for workflows. It does however still leave some edge cases that would be good if a service could expose additional methods externally. One reason for this limitation was that it is difficult to secure arbitrary methods properly. The new https://github.com/feathersjs/hooks general purpose hooks make this much easier.

This is why starting at v5, in addition to the existing service methods, it will be possible to register your own custom methods. A custom method has a name and fixed parameters of (data, params) where data is the payload and params is the usual service method parameters (including things like provider, authenticated user, query etc.). Method information can be passed as options to app.use (app.use(path, service, options)). This will also allow to more easily disable existing service methods (or their events) externally.

Example

class MyMessageService {
  async create (data, params) {}

  async findOne (data, params) {}

  async resendNotification (data, params) {}
}

app.use('/messages', new MyMessageService(), {
  methods: {
    create: {},
    findOne: {
      external: true
    },
    resendNotification: {
      external: true
    }
  }
})

The available options are:

  • external: If this method should be exposed via transports externally. Otherwise it will just become a normal hook-enabled method internally
  • event: The event to send when the method completes

Calling via transports

  • Via REST a custom method can be called using the POST method and the X-Method-Override header. data will be the body payload.
  • Via websockets the method will be available as socket.emit(methodName, 'messages', { username: 'dave' }, query)
  • Feathers does not have an introspection mechanism, so when using the Feathers client, custom methods will have to be defined again explicitly on the client
  • In GraphQL the method will be exposed as a mutation under a unique name
@daffl daffl added Core Feature labels Jun 2, 2020
@daffl daffl added this to the v5 (Dove) milestone Jun 2, 2020
@J3m5
Copy link

@J3m5 J3m5 commented Jun 2, 2020

I think that this is a great addition that will allow more flexibility for building APIs and help keeping the hooks lean and simple.

@webafra
Copy link

@webafra webafra commented Jun 3, 2020

hello .

thanks to public this option.

where route findOne in rest api ?
/message/:id ?

@bertho-zero
Copy link
Collaborator

@bertho-zero bertho-zero commented Jun 3, 2020

I think that a custom method must also be able to have as arguments id, data, params or id, params.
To determine if there is id or not, we could apply this mechanism also on /messages/:id.


There is currently a way to expose custom methods to the desired routes with service.methods & httpMethod: #924 #945
The downside is for client-side use, the routes are just not predefined.

const { rest } = require('@feathersjs/express')

class MyMessageService {
  methods = {
    findOne: ['data', 'params'],
    resendNotification: ['data', 'params']
  }

  async create (data, params) {}

  @rest.httpMethod('POST', ':__feathersId/find-one')
  async findOne (data, params) {}

  @rest.httpMethod('POST', ':__feathersId/resend-notification')
  async resendNotification (data, params) {}
}

app.use('/messages', new MyMessageService())

We could do without service.methods and trust the parameters named by @feathersjs/hooks but it is no longer possible to know the name of the arguments before executing the function since feathersjs/hooks#37.

@daffl
Copy link
Member Author

@daffl daffl commented Jun 4, 2020

Agreed, supporting an id does make sense. I'd however like to avoid any route mapping that only works for a specific transport. The HTTP specification is also fairly clear on that a URI should only specify the resource location (service + resource id) and not contain any actions (verbs).

@J3m5
Copy link

@J3m5 J3m5 commented Jun 4, 2020

I also prefer keeping the service options as a separate object.

@bertho-zero
Copy link
Collaborator

@bertho-zero bertho-zero commented Jun 4, 2020

@daffl I agree, the only thing that bothers me is the constraint of fixed parameters.

To determine if there is id or not, we could apply this mechanism also on /messages/:id.
To determine if there is data or not, we could use the verb GET rather than POST.
Params is always there as the last argument.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
4 participants
You can’t perform that action at this time.
X Tutup