Matt Hamann
Matt Hamann

Reputation: 1668

Building a "flat" API in loopback

When building an API in Loopback, I want to have relatively flat, authentication protected routes like: /users, /orders, etc. So, if I'm a user and I have some orders, I should be able to simply call /orders or /orders/:id to get one or more of the orders for my account.

Using Loopback's model relationships, I could do something like: /users/:userId/orders relatively easily, but there are definitely cases where I don't want to add in that complexity to the URL structure.

Is there a good, recommended way to do something like this? Seems like there should be a way to set this up such that the current user's access token tells Loopback which records it has access to and then can return only those.

Upvotes: 2

Views: 104

Answers (2)

Matt Hamann
Matt Hamann

Reputation: 1668

The solution to this problem wasn't terribly obvious, but I came up with something that works well enough, though it's a bit "manual."

First, in my case, I always have access to the user via req.user, which is a fairly common Node.js / Express pattern. I believe libs like Passport follow this as well.

Then, I created a generic helper method called that looks like this:

module.exports.sourceUser = function(ctx) {
  var req = ctx.req;

    if (!req.user) {
        var err = new Error('You must pass valid credentials to call this API.');
        err.statusCode = 401;
        throw err;
    }

    return req.user;
};

This will ensure that APIs I attach this method to will require user authentication, rejecting any request that doesn't have a valid user cred.

Next, since I'm defining flat APIs, I'm just defining my own remote methods (not using built-in Loopback ones). Each remote method def looks like this:

Model.remoteMethod('getRecord', {
    http: {
        verb: 'get',
        path: '/:id'
    },
    accepts: [
        { arg: 'id', type: 'string', required: true, http: { source: 'path' } },
        { arg: 'user', type: 'object', http: helpers.sourceUser }
    ],

    returns: [
        { arg: 'model', type: 'model', root: true }
    ]
});

You'll need to adjust that code to match your model names, etc. Also, notice the usage of helpers.sourceUser which is the thing that ensures we have a user and then provides that user as an argument to the API implementation.

Finally, I use a where clause in my database queries, etc to ensure that a user only gets/sets their own records (e.g. where model.user == user.id).

Hope this helps someone else...

Upvotes: 1

Ebrahim Pasbani
Ebrahim Pasbani

Reputation: 9396

/users/:userId/orders is needed for admin area e.g You should create a base route such /me for your situation.

Upvotes: 1

Related Questions