This package is deprecated. I will fix bugs, but will neither add new
features nor ensure future Meteor compatibility. Everything that this package is
capable of can now be accomplished using
iron-router
server-side routes and
core Meteor packages (for example accounts-password
).
In fact, some folks seem to have done just that with this
restivus
package. It shares
all the features of reststop2
, with a similar API and planned support for
Meteor 0.9.0+. Check it out!
Thanks for using
reststop2
.
Reststop2
RESTstop makes it easy to create RESTful APIs built on top of Meteor, for use with external systems (or if you're just too lazy to get DDP+SRP working).
It is essentially a fork of Meteor Router, with API-specific modifications:
- It doesn't come with all the front-end routing.
- It makes sure it's run higher in the stack so that your routes aren't ignored.
- You can authenticate users via the API, and access
this.user
.
Writing An API
Here's some simple API methods:
if (Meteor.isServer) {
// Global configuration
RESTstop.configure({
use_auth: true
});
// Maps to: /api/get_user
RESTstop.add('get_user', function() {
if (! this.user) {
return { is_loggedin: false };
}
return {
is_loggedin: true,
username: this.user.username
};
});
// Maps to, for example: /api/get_num/42
RESTstop.add('get_num/:num?', function() {
if (! this.params.num) {
return [403, {
success: false,
message: 'You need a num as a parameter!'
}];
}
return this.params.num;
});
// Maps to: /api/posts
RESTstop.add('posts', { require_login: true }, function() {
var posts = [];
Posts.find({ owner_id: this.user._id }).forEach(function(post) {
// Modify the post here...
posts.push(post);
});
return posts;
});
}
Configuration Options
The following options are available for RESTstop.configure
:
use_auth
- Default: false
- If true,
/login
and/logout
routes are added. You can also accessthis.user
, which will befalse
if not logged in.
api_path
- Default: 'api'
- The base path for your API. If you use 'api' and add a route called 'get_user', the URL will be 'http://yoursite.com/api/get_user/'
pretty_json
- Default: false
- Render formatted JSON in response.
bodyParser
:- Options for Connect.bodyParser
Route Options
The following options (2nd parameter) are available for RESTstop.add
:
require_login
- Default: false
- If true, the method will return a 403 if the user is not logged in.
method
- Default: undefined
- A string ('POST') or array (['POST', 'GET']) of allowed HTTP methods.
URL Structure
The path
is the first parameter of RESTstop.add
. You can pass it a string or
regex. If you pass it test/path
, the full path will be
http://yoursite.com/api/test/path
.
If you want to pass in parameters, use a :
. So, post/:id
will match things
like api/post/123
. You'll be able to access the value using this.params.id
.
If you want to make a parameter optional, use ?
. So, post/:id?
will match
both api/post
and api/post/123
.
If someone accesses an undefined route, by default a 404 and {success: false,
message: "API method not found"}
. You can overide this by using *
as your
route, which acts as a catch-all.
Method Context
Each method gets access to:
this.user
- The user object. It's only available if
use_auth
istrue
. If not logged in, it will befalse
.
- The user object. It's only available if
this.params
- A collection of all parameters. This includes parameters extracted from the URL, parameters from the query string and POST'd data.
this.request
- The Connect request object
this.response
- The Connect response object
Response Data
You can return a raw string:
return "That's current!";
Or, a raw JSON object:
return { json: 'object' };
Or, a raw array (must wrap it in another array):
return [ [ 'red', 'green', 'blue' ] ];
Or, include a status code by using an array with the status code as the first element:
return [404, { success: false, message: "There's nothing here!" }];
Or, include a status code AND headers (first and second elements, respectively):
return [404, { 'Content-Type': 'text/plain' }, { success: false, message: "There's nothing here!" }];
Or, skip using a function at all:
RESTstop.add('/404', [404, "There's nothing here!"]);
Accessing Server Methods
You can access server methods using RESTstop.call(this, 'method_name', args1, ...])
:
result = RESTstop.call(this, 'method_name', arg1, arg2);
Or using apply
:
result = RESTstop.apply(this, 'method_name', [arg1, arg2]);
You can also get published data in a similar manner:
result = RESTstop.getPublished(this, 'method_name', [arg1, arg2]);
result.fetch() // You'll need to manually fetch the results
If you have use_auth
on and the user is authenticated (see above), you'll be
able to access this.userId
and Meteor.user()
as normal.
(Note: This all seems to be working, however it's the hackiest part of this whole package -- be very cautious and test well.)
Consuming A RESTstop API
The following uses the above code.
Any results specified by RESTstop (mostly errors) will include a JSON object
with a boolean named success
and a string called message
.
Basic Usage
We can call our get_num
the following way. Note the /api/
in the URL
(defined with the api_path
option above):
curl --data "num=5" http://localhost:3000/api/get_num/
Or (using the optional :id
from the URL):
curl http://localhost:3000/api/get_num/5
Authenticating
If you have use_auth
set to true
, you now have a /login
method that
returns a userId
and loginToken
. You must save these, and include them in
subsequent requests.
(Note: Make sure you're using HTTPS, otherwise this is insecure. In an ideal world, this should only be done with DDP and SRP, but, alas, this is a RESTful API.)
curl --data "password=testpassword&user=test" http://localhost:3000/api/login/
The response will look something like this, which you must save (for subsequent requests):
{ success: true, loginToken: "f2KpRW7KeN9aPmjSZ", userId: fbdpsNf4oHiX79vMJ }
Authenticated Calls
Since this is a RESTful API (and it's meant to be used by non-browsers), you
must include the loginToken
and userId
with each request.
curl --data "userId=fbdpsNf4oHiX79vMJ&loginToken=f2KpRW7KeN9aPmjSZ" http://localhost:3000/api/posts/
Or, pass it as a header. This is probably a bit cleaner:
curl -H "X-Login-Token: f2KpRW7KeN9aPmjSZ" -H "X-User-Id: fbdpsNf4oHiX79vMJ" http://localhost:3000/api/posts/
Thanks To
Thanks to the following awesome projects, which I borrowed/stole ideas and code from: