I am about to start implementing an API in my Rails application that will respond to the requests it receives from my mobile application. Whenever I check resources about creating an API with Rails, everyone seems to talk about versioning from the get go. I was wondering what could be the drawbacks of not versioning from the get go, besides I might regret it if I ever decide to go public with the API?
Versioning helps you deal with problems. If you have a product named Foo, you may start at 1. (Foo 1) That first version was perfect and you decide to make a change. Your next version will be Foo 2. You have found a bug in Foo 2, and you can't fix it. Therefore, you revert back to Foo 1.
If you didn't version your product, then how would you know which Foo is stable? You may look at modification times, but that's not good enough.
Also, what if you go public (as you mentioned), and you haven't versioned the product? Then you will have to adapt and get used to the idea of versioning.
Related
Can somebody please explain me what exactly is API versioning and why is it needed. I know how to create versions for api on a rails web app, but I really want to know why is it needed. Before somebody downvotes or flags or anything, I googled , I couldn't find any satisfying answer. I would really appreciate it if somebody answers this.
API versioning allows you to have multiple versions of your API and use them at the same time. With this solution, you are assuring backward compatibility for all of the applications integrated with your API.
Simple example
Your API is used by 10 different applications. You are using Basic access authentication, but you noticed that it could be done better. So, you decided to use modify this and use Oauth.
No API versioning
You will have to wait for all of 10 applications to implement changes before releasing the new API version. Otherwise, you will lose the integration. Of course, you can use if/else statements in your code to distinguish which authentication method should you use but this will be not elegant.
API versioning
You can release new API version whenever you want. Then, you can inform your client, that the old API will be deprecated in 3 months, so they have time to implement changes on their side.
Also, you can ask them to add a param to all requests (to choose which API version they will use), and you can set it by the default to the new version. That will allow you to avoid problems with new applications that want to use your API.
Summary(in my opinion):
Pros
1. clean and elegant code (without additional if/else statements)
2. backward compatibility
Cons
1. sometimes you have to duplicate your code
2. it might look like a complex solution at the beginning but don't be scared
Here you can read about two options of API versioning - URL param and HTTP header
I hope that my explanation is clear and helps you understand API versioning
The main reason for versioning your APIs is to provide a constant structure for everybody using them. Let's say you define an initial API for your service (v1) that you send out to your clients. After some time your app changes, and you maybe want to exclude some fields/add new ones. This would be a problem for the client, since their implementation of your API might break if some fields that they are expecting is missing. So you create a v2 with those features, without breaking the initial functionality.
I have my main app and admin app built as a microservice, they are communicating via api. I want to share some constants between those two apps.
For example I have User model that can have role Owner or Regular. In admin app I can search Users and in this search I have dropdown with hardcoded user type (Owner, Regular). This is okay, but when I change naming (e.g. Regular -> Standard) I have to update my Admin app also.
To avoid changing admin app every time I change some core naming in my main app I want to somehow share those constants, so every change in main app will change Admin at the same time.
For now I found 2 solutions, both with pros and cons:
First is sending constants from main app to admin via json api. I have build a class that will fetch and store all constants in class variable, so it's available from every part of the the app. The good thing about this solution is performance (thanks to memoization it's only one api request) and it's easy to use later. The bad thing is I have no idea how to handle tests in this case. Of course I cannot let my tests make request to main app and stubbing this request makes the whole idea pointless, because after every change of constants in main app I will need to change tests in admin app.
Second approach I thought of is building a gem that will store all constants. It's very easy to implement, but this means I will need to make changes to this repo every time I want to change constants in main app. Also I work with big team and they won't be happy that they have to work on 2 repos at the same time.
What do you think about those solutions? First one seems to be perfect for me except tests, so maybe you have some ideas how to stub those constants without real values? I haven't tried gem solution yet so if you see some obstacles please let me know.
Maybe there is another better solution to this problem?
I'm not sure if there might be a better solution in terms of structure of the services themselves, for example one of the two services could have an API which returns the constants for the other to use.
But to answer the question you could use an engine, or more likely for simple sharing of constants, a gem. You can create a new gem with bundle gem <GEM NAME> and then add it to the Gemfile of both apps.
You will need to either have a gem server (e.g. geminabox) or just point directly to your code repo, e.g.
gem 'my-gem', git: 'git#my-server.com:git/my-gem`
Personally speaking I'd go with the API returning the constants because you might want to rewrite one service in another language in which case sharing a gem falls down.
write a gem and include it in the different services.
if they run on the same machines, another possibility is env vars.
Keep it in a DB/storage, i.e. memchache/redi. Load the relevant constants on each service init.
I have a Rails backend that serves multiple clients:
Angular JS App
iOS App
At first, I only had the iOS App, and used classical API versioning in rails when substantial changes were brought to the client (naming my versions v1, v2 and so on).
When we came up with the Angular front end, we needed a specific API, so I did a new version that I would use only with Angular (let's say v5).
At that point, v1 through v4 are dedicated to iOS, and v5 to Angular.
Obviously, I can simultaneously update my backend along with my Angular API, since both are served to the client every time they access the website. Therefore, there is no need for the Angular API to be versioned.
However, I'm at that point where I need to update my iOS API, and it is starting to get very wrong : v1..v4 are for iOS, and v5 is taken by Android. So the supposed next iOS API should be... v6? Definitely wrong.
I wanted to know if it was possible to name an API version angular, and thanks to this answer on SO I understand why this is not possible. I'll quote the interesting part for my case:
Ryan Bigg wrote:
I couldn't figure out how to get /api/asdf/users to match, because how do you determine if that is supposed to be a request to /api/<resource>/<identifier> or /api/<version>/<resource>?
Now you understand better my situation, here's the question.
The question
How am I supposed to manage multiple APIs, but which are not really like versions?
Is it possible to subdivide API versions? Like:
api/angular/v1
api/ios/v5 (moving v1..v4 to api/ios/ as well)
api/android/v1
Is there any best practice to do so? Or should I just be using the current API versioning system, knowing which version corresponds to which client?
I think in the end this is a design issue. Your API should be thought of as the methods to access your model in networked MVC. Just as in any MVC the model should be independent of it's view and controller. I think having a different API per client is almost like having a different model for each client, which I don't think is what you are intending.
As an example, (I am assuming your API is Restful), a GET request to
api/v1/user/1
might return a json "view" of user 1, each of your clients should be written to work with that same output. If you decide you will no longer support this, or add or remove some content from the returned json that would break an existing client, then you might bump the version and implement the change on that version
I would suggest your next API version expose all of the resources needed by any clients you currently have, and then overtime update your clients to work with this new unified API. If you find your self making changes to your model (API) that would break an existing client, then you can release a new version, allowing existing clients to continue to work with the old API and new or updated clients can pick up the new version.
A set of automated tests on your API will help identify when you break an existing client.
I am not expert at this, but I did (as a learning project) create a rails app with an api, and I was able to use the same api for android and ios.
In regards to your routing issue quoted from Ryan Bigg, you can resolve it by applying a constraint to the route. For example:
get /api/:type/:version/users/all, to: "users#index", constraints: {type: /(ios|angular)/, version: /\d+/}
Ensuring the the version is a number and the type is either "ios" or "angular", you can easily set up the routes for two separate APIs.
To answer the questions as to whether or not it is possible--yes. Anything is possible, especially with Ruby!
I, first of all, would not have two separate APIs to manage. However, if I did, I would create separate namespaces for each API and name the as AngularAPI and IosAPI. I would just route to them separately. The correct answer will depend on how exactly you've set up your API system.
How would I maintain two separate APIs? (from the comment)
It depends on how substantially different the APIs are. If there are only a few differences, it would make sense to simply use a different controller with different routes. For example, AngularUsersController and IosUsersController. They would both extend a ApiUsersController and alter only what was needed for each platform. This would still allow for proper versioning as it grew, and also keep code duplication at bay.
I'm working on a project that has two different Rails apps. One is the web app, and another is the API app. They both use the same database. How, or what, is the proper way for them to use the same models and schema? Currently, it appears as though the model files are copied from one app to the other, and they seem woefully out of synch when checking the differences.
A few options come to mind:
Git submodules. Store your models in a separate repo (or repos), and then include it as a Git submodule in each app. This allows you to reuse them without having redundancy, though you’ll likely have to ensure that you keep which revision each app is using in-sync.
Make it one app, with two distinct parts. Maybe your API could be a simple Sinatra (or other microframework) app, and reside within the Rails app, and then mounted to a path/subdomain. This may be the least amount of work and the simplest, but it’s also probably the most cluttered.
Use your own API. You already have an API, why not use it? Your front-end app can just talk to that and doesn’t have to have any the model logic inside it. This also has the nice side-effect of ensuring that your API works and is full-featured, as you’re now a consumer of it instead of just third-parties.
Which one you pick is up to you, though I happen to prefer the last. While it’s probably more work, you’ll likely end up with an overall better design by making your business logic external (and making MVC violations harder, if not impossible).
We want to start letting our users help us test our feature changes before a wider release. Our rails app already has roles but I don't know how to we should go about implementing a beta feature without moving the whole app over.
Some issues I can't think of solutions to:
A beta feature may require a database migration. How can you handle this when it could cause problems with the existing app?
Changing templates and the css/sass will likely change it for existing features too.
Changing the underlying model code could break existing controllers / interfaces that rely on it.
One solution (a bad option) is to code in the new feature and wrap it in logic that only shows/uses it if the user has the "beta" role. The problem with this is when you finally take it live, you could have a lot of unwinding/changing to do. This is both a waste of time and could introduce bugs.
Another solution is to run a separate "beta" branch of the app off a subdomain and route users with the beta role to it. The problem with this is that the complexity of ssl certificates, email links and other domain dependent issues make this a bit of a maintenance pain (though not as bad as the first solution).
How can I offer this most efficiently so as to minimize the additional work to maintain and then switch the beta to the full version?
I think the only reasonable chance of these kind of tests work without affecting the current application would be using a "staging" environment and really just redirect the beta users to that environment.
Compared to problems with domain related features it is really nothing compared to migrations/functionality problems.
On our company we do exactly that, but we don't have the beta users thing, but without an separated environment it will be pretty much inviable to keep new features from messing with current features.
For the features, just use different branches for those new features and create that "staging" environment from that branch, once the features have been tested you just merge them to HEAD and new feature is there :]
One possibility to consider: making destructive (i.e. one-way, non-reversible) changes to your model may be problematic for reasons beyond inhibiting your ability to provide this beta functionality. For example, it may difficult to backout from a change if you have a problem during the migration.
Instead, I would recommend looking at ways to only add to the model: add columns while leaving old columns in place for backward compatibility, version stored procedures if you're using them, etc. If you need to alter column data types, instead create a new column of the target data type, then have your migration also copy existing rows data from the old column to the new column in the new format. You can then perform your database migration in your test environment and confirm that both the old and new versions of the application continue to work with the database changes.
One potential way to serve up multiple versions of your application is to use an alternative respond_to format for your beta site. You could create a method in your ApplicationController to check if the user was in the beta. If true, you can override the request.format value and in your respond_to block have a "format.beta" type response.
The challenge with this approach is the controller logic. Without embedding some kind of switching logic within your controller methods, you won't have a way of altering the controller code path. This may not be a major problem, depending on how many controller changes you have.
(By the way, it looks like we have very similar names! :-) )
What I can think of is something like having a user_type column in your users table. so that you can flag them as beta users. (Even you can set this flag as default so that you dont need to change the existing code. All the new users who are creating will be beta users.)
For this i'm assuming
You are giving all the features to your beta users
Beta users will be having the same functionality which will be having by normal user in future.
** Only advantage is that you can filter beta users as and when they are login. Upon that you can do something like allowing to login or not etc..
When you are switching to the full version just update beta users as normal users
I dont know how this is applicable to your scenario.
thanks
sameera
I personally don't think it's a bad idea to wrap the code with a check for a user having the beta role. It will be quite easy to search for all of the calls to, for example if current_user.authorized?(:beta) and the remove them entirely.
One thing I am thinking about doing is setting up a beta "staging" environment that is actually the production environment. The idea would be to have beta.mydomain.com and then send users there that want to get features early. Basically, it would just be a branch that gets deployed to the beta site which is live.