giapnguyen74 / nextql-feathers

NextQL plugin for Feathers
MIT License
10 stars 1 forks source link

Are nested services supported? #1

Open eddyystop opened 7 years ago

eddyystop commented 7 years ago

Interesting project.

Assume we have 2 services containing this data.

postings = [
  { _id: '100', message: 'Hello', posterId: '2000', readerIds: ['2001', '2002'] }
];
users = [
  { _id: '2000', name: 'John' },
  { _id: '2001', name: 'Marshall' },
  { _id: '2002', name: 'David' },
];

Using something like postings.get('100', { ... }), can you get back something similar to (no need to be exact) this

{ message: 'Hello', posterId: { name: 'John' }, readerIds: [ { name: 'Marshall' }, { name: 'David' }] }

Thanks.

giapnguyen74 commented 7 years ago

I going to code a sample about nested services soon. Basically it is NextQL feature that same with how GraphQL resolve nested data. You only need define "computed" fields.

const postService = {
 fields: { _id:1, message: 1},
 computed: {
   //let compute poster from post
   poster(post){
     return db.collection('users').findOne({ posterId: post._id});
   },
   //let compute readers from post
   readers(post){
     return db.collection('posts').find({ readerIds: post._id });
   }
 }
}

const userService = {
 fields: { _id: 1, name: 1 }
 computed: {
   // let compute posts from user object
   posts(user){
    return db.collection('postings').findOne({ posterId: user._id});
   }
 }
}

Then you can query poster from user , even poster -> user -> posts

posts.get('100', {
  query: {
    message: 1,
    poster: {
      name: 1,
      posts: {
        _id: 1,
        message: 1
      }
    }
  }
}) 
giapnguyen74 commented 7 years ago

Dear @eddyystop , Please check your case at Nested service sample Run

async function test(){
    await app.service('users').create([
        { _id: '2000', name: 'John' },
        { _id: '2001', name: 'Marshall' },
        { _id: '2002', name: 'David' },
    ]).catch(() => true); //bypass if already created

    await app.service('posts').create([
        { _id: '100', message: 'Hello', posterId: '2000', readerIds: ['2001', '2002'] }
    ]).catch(() => true); //bypass if already created

    const post = await app.service('posts').get('100',{
        query: {
            _id: 1,
            message: 1,
            poster: {
                name: 1
            },
            readers: {
                name: 1
            }
        }
    });
    console.log(post);
}

test().then(() => true, console.log);

Result

node samples/posts/index.js
{ _id: '100',
  message: 'Hello',
  poster: { name: 'John' },
  readers: [ { name: 'Marshall' }, { name: 'David' } ] }

Is it ok for you?

eddyystop commented 7 years ago

I understand the idea. Thanks.

Feathers would tend to use app.service('posts') instead of db.collection('posts'), the computed functions need to have access to app. What do you recommend?

giapnguyen74 commented 7 years ago

Right, I will insert app object into context next patch. Then you can access app from computed function (current, context is feathers params)

poster(post, params,{ app }) {
   return app.service('users').get(post.posterId);
}

You could check my sample - currently I use feather service directly (without app).

poster(post) {
    return Users.get(post.posterId);
},

But in my opinion, it depend on your use cases. If you use computed field replace for $populate ; it is make sense to query directly from database - it is best performance. If you want to benefit from featherjs data access layer, use service directly like my sample. Finally call app.service if you have special reason. Other case to not use app.service if you want to resolve intermediate objects, which not expose for client access. For example: you don't want allow query posts directly , so client should use users service to query their own posts.

eddyystop commented 7 years ago

Thanks for your explaination. I'm interested in remaining database independent. So using a Model or getting app would achomplish that.

Thanks also for introducing app.

giapnguyen74 commented 7 years ago

So use service is best for u. I extremely against use app.service. nextql-feathers is a wrapper over original feathers service. When u call app.service, it go through app -> nextql -> original service. So if use in computed, it will follow the route again without additional benefit over call original service directly. Don't forget computed fields could be n+1 issue, if your query result 10 posts; will be 10 more calls to get readers.

eddyystop commented 7 years ago

I understand. Thanks.

On Fri, Aug 4, 2017 at 4:35 PM, giapnguyen74 notifications@github.com wrote:

So use service is best for u. I extremely against use app.service. nextql-feathers is a wrapper over original feathers service. When u call app.service, it go through app -> nextql -> original service. So if use in computed, it will follow the route again without additional benefit over call original service directly. Don't forget computed fields could be n+1 issue; if u query 10 posts each post have 10 readers, it is 110 calls!!!.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/giapnguyen74/nextql-feathers/issues/1#issuecomment-320347249, or mute the thread https://github.com/notifications/unsubscribe-auth/ABezn8MqsJunb9ckDwHxu9LSi9vmhJaMks5sU4CMgaJpZM4Os1J1 .