Skip to content
This repository has been archived by the owner on Apr 20, 2018. It is now read-only.

hook on non-service apps? #39

Closed
wuyuanyi135 opened this issue Feb 15, 2016 · 11 comments
Closed

hook on non-service apps? #39

wuyuanyi135 opened this issue Feb 15, 2016 · 11 comments

Comments

@wuyuanyi135
Copy link

I want to authenticate the user before the request
app.post('/import', import)
which is not a feather service.

Is it possible to use the hook

import.before({
  [
    authHooks.verifyToken({secret: 'feathers-rocks'}),
    authHooks.populateUser(),
    authHooks.requireAuth()
  ]
})

instead of writing the middle-ware by myself?

@marshallswain
Copy link
Member

Why would you not convert /import to a service?

@wuyuanyi135
Copy link
Author

Yeah that could be.
So I will use create:function(...){...} to pack this

@marshallswain
Copy link
Member

That's what I would recommend. It didn't take long for @daffl to convince me that you can pretty much map anything into services and REST methods, even if you need to spread it out across multiple services for the API to make sense.

@wuyuanyi135
Copy link
Author

What if I want /import to be a sub-app router?
For example, I will have /import/user , /import/data ,etc. I want all the endpoints to be accessed with auth. So I want authenticate the user before /import.

@marshallswain
Copy link
Member

You could try using the feature that @daffl added yesterday where you can turn off the writing to the database on /import by setting hook.result manually in a before hook, then do a switch based on whatever data is coming in to make an internal API call to the other service. The only caveat would be figuring out how to communicate with the other sub-app. You'd have to use the other sub-app's service in the current sub-app and maybe block it externally. It seems like I'm missing something that would be in the way of letting that work correctly.

Or you could re-analyze if the desire for /import to be a gateway for all other imports is an actual need. I say this because I tend to get my head wrapped up in thinking that API consumers would appreciate it that way, but in reality, if they are doing all of the work to prepare a different format of data already, changing the URL they submit it to would be the least of their worries. But I don't know the details of your situation. :)

@wuyuanyi135
Copy link
Author

Yes. I need rethink my logic. I do not need tons of apps to connect with gateway. :)

Thank you

@ekryski
Copy link
Member

ekryski commented Feb 16, 2016

@wuyuanyi135 another option might be to create a user-import service, a data-import service and just map them to /import/user, import/data respectively.

app.use('/import/user', userImportService);
app.use('/import/data', dataImportService);

I don't know what your imports do but you could write a generic import service that does whatever you need to do to import stuff. Writing custom services is actually pretty easy. They don't have to be database backed. They could talk to a file system, do stuff in memory or call remote APIs. You just implement specific service methods to automatically support the REST and socket transports.

So if you did that you would just have:

app.use('/import/user', importService);
app.use('/import/data', importService);

@jiangts
Copy link

jiangts commented Sep 5, 2016

I'd like to bring this back up, as I there are many cases where my application domain does not simply map to the current service model (which is very CRUD biased). A simple example is for a service that accepts multiple commands. E.g. I'm building a fancy multi-user chat app, and I want to accept the following commands:

  • user creates chatroom as owner
  • user transfers control of chatroom to another user
  • user joins existing chatroom
  • user abandons chatroom
  • user rejoins chatroom as owner
  • user closes chatroom

Ideally I wouldn't want to describe each of these actions as nouns, but rather as verbs. As these methods are all related, I want them to exist in a single class, rather than create a new class for each action I want to support. To reiterate, although I could try to proliferate classes and try to map them to CRUD, it seems icky to me as it obscures the problem domain.

Any help here on how to hook non-CRUD methods would be appreciated!

@marshallswain
Copy link
Member

I'm not going to be much help on hooking non rest methods, but when a use joins a chat room, what in your database changes? If you are storing the memberships directly in the chat room document then that maps to patching a chat room. If you are using an intermediary table to track which users belong in which chat rooms, then you are dealing with memberships, and not chat rooms, at that point. So it might be cleaner to create a memberships service for those methods.

@daffl
Copy link
Member

daffl commented Sep 5, 2016

I agree with @marshallswain. With the right way of approaching the pattern I don't think there are really many cases where the service model couldn't be applied. I know it seems more intuitive to think in verbs but it's actually a little weird because the platform we are all building for (the web) is based on resources (nouns) and a number of well defined verbs just like services are (those restrictions on the service interface are intentional).

In your examples I'm not seeing why it couldn't be done as a service:

  • user creates chatroom as owner: app.service('chatroom').create(data)
  • user transfers control of chatroom to another user: app.service('chatroom').patch(id, { owner: newUserId })
  • user joins existing chatroom: app.service('users').patch(userId, { rooms: newRooms })
  • user abandons chatroom: app.service('users').patch(userId, { rooms: newRooms })
  • user closes chatroom: app.service('chatroom').remove(id)

For more complex interactions you can also create one-off services that do several things at once. For example, I often have a dashboard service that aggregates a bunch of data and sends it all at once or a service with just a create that accepts a bunch of data and then performs a number of actions.

@jiangts
Copy link

jiangts commented Sep 5, 2016

Thank you @daffl and @marshallswain. Although I see @daffl's mapping could work, I still find it to be obscuring the application domain. In particular, if I'm inspecting the application logs after the fact, it's really hard to look at the logs and understand user intent (a lot of forensics to differentiate between all the overloaded patch operations!), and the names for what I'm actually intending to do get lost by forcing the domain to be mapped to a RESTful API.

I think I'll try @daffl's suggestion on building a one-off service with just a find or one with just a create that can perform a bunch of different types of actions based of a query param or something like that!

And on a slightly unrelated note, when you perform a bunch of actions with the overloaded create service method, is there a good way to chain calls to other services with transaction support?

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants