Hi,
I haven't found the answer in the issues stack neither in the documentation, so here's my question.
Is it possible to prefix all routes to API exposed resources by a string which can be used either to scope the API by domain either for versioning purpose ?
Cheers and good job guys.
Max
Common question. Should really be in the docs.
Here's how to do it:
Add a config.local.js next to your config.json file and use the following content:
var p = require('../package.json');
var version = p.version.split('.').shift();
module.exports = {
restApiRoot: '/api' + (version > 0 ? '/v' + version : ''),
host: process.env.HOST || 'localhost',
port: process.env.PORT || 3000
};
This will set your api path as: /api/v1 using your app's major version. If your app's major version is 0, then the path is simply /api.
p.s. config.json is still required due to an existing bug.
I was just going to add this to the docs, but I could not get it to work. I tried it in config.local.js but no dice -- even tried putting the code in the rest-api.js boot script. Seemed to have no effect. And I _did_ change the version in package.json to 2.0.1. Requests to http://localhost:3000/explorer/v2/ were never handled.
@superkhau Can you take a quick look and see if it works? If not is there an easy way to do this? If so, please create an issue and tie it back here. Once we have a solution, Rand can update docs.
@simoami's solution is correct, thanks for your contribution.
@crandmck http://localhost:3000/explorer/v2 makes no sense to me, I have no idea what you're trying to achieve there.
Anyways, you can verify the solution by performing the following steps:
slc loopback:model CoffeeShop)http:localhost:3000/explorer)CoffeeShop model (if you look at the GET endpoint for example, you will see the _Request URL_ will display http://localhost:3000/api/v1/CoffeeShopReassigning to @chandadharap for next steps.
@superkhau
So I think what you're _trying_ to say is that changing the REST API root does not (a) affect the URL of the API Explorer itself or (b) affect the root route handler because that is set by boot/root.js.
@crandmck a) It's not supposed to effect the API explorer (by design) and b) the root route handler has nothing to do with the API routes (the ones /api/model), so this part is irrelevant. We are versioning the API, not the non-API routes.
Added to docs: http://docs.strongloop.com/display/LB/Versioning+your+API
@simoami's solution is correct, thanks for your contribution.
Glad to give back to this awesome framework.
Hi,
Sorry for the delay.
This solution does not exactly fit my needs.
For example, I would like to have several domain names, let's say domain1, domain2 and domain3, which are all corresponding to distinct group of endpoints / resources.
I would like to have the ability to call those resources like this :
/api/domain1/users/list
It would follow the syntax :
/<restApiRoot>/<domainName>/<resource>...
Can I do that inside the config.json ?
Max
For example, I would like to have several domain names, let's say domain1, domain2 and domain3, which are all corresponding to distinct group of endpoints / resources.
My advice is to create one loopback application per domain. That way you can also customize the server configuration on a per-domain basis, e.g. implement a per-domain authentication.
@bajtos Yup, does not fit with our DDD approach http://en.wikipedia.org/wiki/Domain-driven_design
I also want to avoid to implement several authentication methods.
does not fit with our DDD approach
Could you please elaborate more on that? I don't see the connection between namespacing model URLs (/domain1/model1) and DDD.
Are you intending to expose the same model on multiple domain paths?
If not, then you can try to include the domain path portion in the "plural" field, e.g.
{
"name": "MyModel",
"plural": "domain1/my-models"
}
Note that this is rather hacky and may break in the future versions of loopback.
We should implement a proper solution, e.g. add an extra model option to specify the domain (namespace):
{
"name": "MyModel",
"namespace": "domain1"
}
Or perhaps
{
"name": "domain1.MyModel"
}
One more solution that works at the moment:
MyModel.sharedClass.http.path = '/domain1/my-models';
@wascou Regarding "I also want to avoid to implement several authentication methods." you could also use an SSO (Single Sign On) provider for centralized authentications.
I'd also recommend looking at the micro-service architecture where it's encouraged to have a separate service per domain. Each service specializes in one and one thing only.
@simoami Our purpose here is to expose APIs to external partners. We already have an internal API platform achieving what you talk about. Our external API must connect to all platforms inside our company. We have considered the fact to purpose directly internal APIs through this external API platform, meaning with no transformation / assembly, but it's not reaching the architecture standard we would like.
Now, let's say that we want to achieve business on this API platform. We have taken the following decisions impacting our solution :
Knowing that, I just want to know how to prefix more precisely access to resource with a domain.
@bajtos Thank you for the input. It seems to be a kind of fix and not a proper use of the framework.
We don't want to expose the same model on multiple domains. A model must be exposed in one and only one domain.
just want to know how to prefix more precisely access to resource with a domain.
If you only worry about the URLs, then the solution with sharedClass.http.path is IMO the right way to go.
MyModel.sharedClass.http.path = '/domain1/my-models';
The missing piece is how to specify this data via model JSON file. I can imagine adding a new section called remoting that would allow that.
{
"name": "MyModel",
"properties": { ... },
"remoting": {
"http": { "path": "/domain1/my-models" }
}
}
@ritch @raymondfeng thoughts?
+1 for a remoting section. Would be pretty easy to add as well... In lib/model.js we need something like:
if(settings.remoting) this.sharedClass = new SharedClass(settings.remoting); // perhaps with a merge of defaults
+1
@wascou, I didn't realize this is to aggregate apis, which is what an api gateway is expected to provide. I do agree that in your setup the api routing flexibility is needed. Also, if you have a load balancer running in front of your infrastructure you can temporarily rewrite paths until loopback gains this feature. sounds like you could get this sooner.
@bajtos I like the remoting section being part of the model config. You might want to also update the yeoman model generator to prompt for an optional path.
Thank you everybody for the answers.
So, I understood that Loopback cannot do that at the moment.
How can I help to see this feature in the framework ?
@wascou You can submit a PR with the proposed changes. ;)
@wascou There's a very simple hack. specify the domain path as part of the plural property and your custom path will work. For example:
{
"name": "user",
"plural": "domain1/users",
...
}
The api is then served from: /api/domain1/users.
As it turns out this is already supported:
{
"name": "brand",
"plural": "brands",
"http": {
"path": "domain1/brands"
},
...
}
@simoami, thank you, I'm going to test that.