visionmedia / express-resource Goto Github PK
View Code? Open in Web Editor NEWResourceful routing for Express
Resourceful routing for Express
we could use lingo.en.isSingular()
to detect and auto-magically support this
How can I nest resources in order to have URL schemas like:
/post/{post_id}/comments
/post/{post_id}/comments/{comment_id}
/post/1/comments
/post/1/comments/54
Currently express-resource uses the :format
parameters for matching requested resource format (.xml
, .json
, etc).
In addition to this, it would be nice to support the HTTP Accept headers. connect-conneg provides a parser for them that organizes the requested formats by preference.
This can already be used with express-resource, but requires some boilerplate. First you need to add the conneg middleware:
server.use require('connect-conneg').acceptedTypes
And then handle the types in the default
content negotiation handler:
default: (req, res, next) ->
for type in req.acceptableTypes
if this.html and type is 'text/html'
return this.html req, res, next
if this.json and type is 'application/json'
return this.json req, res, next
next()
It would be very useful if the default implementation of default
did this, instead of falling back to 406
error.
currently:
var users = app.resource('users', actions);
users.get('/foo')
is GET /users/foo
, not GET /users/:user/foo
as some would expect, however we do need a way to define both. I propose that users.get('/foo/bar')
is /users/foo/bar, and
users.get('foo/bar')is
/users/:user/foo/bar`
Just spent a bunch of time debugging what turned out to be caused by us reusing some code that said req.params.id
for a non-top-level resource.
IMHO, having the API change based on what resource you're in doesn't exactly meet the principle of least surprise. =D
Also, FWIW, from a usage perspective, id
is a much better variable name because it's actually a string, whereas e.g. forum
or w/e isn't accurate, because it's not a Forum
object (or whatever my model-layer objects are).
So here's my ask for the default ID to be consistent and simple. You could still pass in a custom ID as an option, etc.
Hi,
I believe the npm registry contains the wrong version of express-resource. While
npm view express-resource
tells me that the version published should be ok:
{ name: 'express-resource',
description: 'Resourceful routing for express',
'dist-tags': { latest: '0.1.0' },
maintainers: 'tjholowaychuk [email protected]',
time:
{ '0.0.1': '2011-02-25T17:15:02.390Z',
'0.0.2': '2011-03-04T00:04:38.603Z',
'0.1.0': '2011-03-29T20:26:15.566Z' },
the contents of /usr/local/lib/node/.npm/express-resource/0.1.0/package/index.js seems to belong to version 0.0.2. For example, it is considerably shorter (~80 lines vs ~200 lines).
Peter
When using both express-namespace and express resource, I get the following error when trying to implement nested resources:
.../node_modules/express-namespace/index.js:64
fn.namespace = curr;
^
TypeError: Cannot set property 'namespace' of undefined
at HTTPServer. (.../node_modules/express-namespace/index.js:64:20)
at HTTPServer.namespace (.../node_modules/express-namespace/index.js:29:6)
at HTTPServer.get (.../node_modules/express-namespace/index.js:57:10)
at Resource.add (.../node_modules/express-resource/index.js:188:17)
at HTTPServer. (...)
at HTTPServer.namespace (.../node_modules/express-namespace/index.js:29:6)
at ...
E.g
var forums = app.resource('forums', require('resources/forums'), { load: Forum.get });
var threads = app.resource('threads', require('resources/threads'), { load: Thread.get });
forums.add(threads);
"express": "2.5.2",
"express-resource": "0.2.4",
"express-namespace": "0.0.4",
~ DELETED ~
Is there a way - other than mounting it as a 'sub-app' - to provide a root mount point for express-resource
? I want all the resources to live at /api/{resources}
, but I can't seem to find a way that feels 'right' to accomplish this.
My usecase is as follows:
I want my resource Ids/Names to be human readable, with all the special chars and dots they include.
I do have a “champions/Dr. Mundo.png”
Thanks to the patch in pull request 27, it does work now when I include a format. So “Dr. Mundo.png” becomes “Dr. Mundo”. Same for .json.
But I want to use “champions/Dr. Mundo” as URL without any appended format, showing the default html.
I could just make it “champions/Dr. Mundo.html” but I don’t want to have that ugly .html everywhere.
So I want the format to be either .png or .json, but not mess up my resource Id otherwise.
Works...
app.resource('admin/users', require('./app/resources/admin/users'), { id: 'user' });
Doesn't work...
app.resource('admin/users', require('./app/resources/admin/users'));
In the "Doesn't work..." example the param will be ":admin/user" instead of ":user".
Express allows res.format
to route a request based upon Accept
header.
However express-resource
sets req.format
which has to be checked separately.
Is there a reason why format specifier (e.g. '.json') cannot or should not be used on create, update and/or destroy requests?
Reason I ask is that I am using the format specifier to cater to various and varied "RESTful" implementations and interpretations.
i.e. support various Grids and the likes...
I have changed the line (around line number 111 of index.js):
if ('get' == method) route += '.:format?';
to:
route += '.:format?';
Initial tests show nothing has broken, and I do have a 'load' function to make sure id is extracted properly.
**Edit:
Added Destroy (delete).
environment: node v0.6.6 on windows with express 2.5.2/connect 1.8.3 and express-resource 0.2.3
Routes in the form of "/users/1/edit" do not work for me (e.g. I get "Cannot GET /users/1/edit" in the browser), whether or not they are a default route (such as "edit") or a custom mapped route (such as "send" in "/users/1/send"). However the other routes seem to work fine, those in the style of "/users" or "/users/1."
Here's a gist containing the setup that fails for me: https://gist.github.com/5490fb68a61f9b80b0fa
because of Object.keys() exporting .new
etc are not order independent, this is definitely a bug
I plan on either updating express-resource for express 3, or possibly writing a standalone resourceful router for express 3 in the meantime. What are your thoughts on an API like this?
Hi,
looks handy express-resource, will it support express 4 too? thanks,
A.C.
I used to set app.all('/*', function (req, res, next) ...
as my last route in order to handle routes that are not found, it used to work fine but now that I use express-resource it seems that static content like .css
, .js
, .jpg
are processed by the router without express-resource they are not processed and app.all('/*'...
handles not found routes properly
If I have a 'countries' resource, then req.params.country is undefined. Instead this is what req.params has ('countrie'):
params: [ countrie: '1', format: undefined ] },
To add custom actions, today I do this (similar, this is adapted so there may be some simple errors):
var controller = require('users_controller');
var resource = app.resource('users', controller);
var custom_user_actions = [
{
name: 'login',
method: 'get', // or post, put, delete
scope: 'element', // or 'collection'
action: resource.login
}
];
// Constructs /users/:user/login for element-type actions, or /users/recent (for example) for collection-type actions
custom_user_actions.forEach(function(action) {
var path = '';
if('element' == action.scope) {
path = '/' + resource.param;
}
path = path + '/' + action.name;
// Calls resource.get, etc
resource[action.method](path, action.action); // Add with the proper method
});
}
First, is there already a better way to do this? If it makes sense, the forEach could be integrated as a method on the Resource object pretty easily. Other approaches?
Not sure how possible this would be, but... It would be super awesome if there was an option to customize the generated URLs on a per-action basis. For instance, assuming we only export new, create and destroy:
app.resource('sessions', require('./sessions'), { paths: { 'new': '/login' } });
... would generate URLs that would map to actions like this:
GET /login -> new
POST /sessions -> create
DELETE /sessions/:id -> destroy
This would be especially cool if it played nice with bodyClasses (issue #11) in such a way that bodyClasses() for '/login' would generate 'sessions new'. This would be ideal.
This works:
var photos = app.resource('photos', photosController)
, posts = app.resource('posts', postsController)
, photosComments = app.resource('comments', commentsController)
, postsComments = app.resource('comments', commentsController);
photos.add(photosComments);
posts.add(postsComments);
However, I am not sure whether this will create problems down the line. Particularly because app.resources.comments
will always be the last nested resource added, and the first one would not be accessible via app.resources
.
Suppose I've done something like:
var users = {
show: function(req, res){
//do something
},
me: function(req, res){
//do something
},
load : function(id, fn){
User.findById(id, fn);
}
};
var userResource = app.resource('users', users);
userResource.map('get', 'me', users.me) //expecting /users/me to do something
That doesn't work. It seems to think that "me" is the ID and passes it to load
, thinking it's going to invoke show
. In fact, I don't know how it would know any better. So I'm thinking there's no way to do custom collection actions; you can only do custom member actions.
So first off, is that right? If so, is this something I should patch?
This is just a matter-of-fact question on the project's status before I invest a lot of effort into using it in my project. Is this project considered production quality? I was in the beginning stages of designing almost this exact thing but also with mongoose schema mapping into resources (which Panta has written that depends on this project) that would save me a month or two of work, if I don't get stuck down a rabbit hole with it. I take DRY to a whole new level - DRO or ABD (Already Been Done) ;)
When i create a restful scaffold in rails like
GET /resource/:id/edit i am greeted with a form that does a POST to /resource/:id.
But for a similar resource if i post to /resource/:id from the /resource/:id/edit form i get a Cannot POST. If this is unavailable how can a web-application update a resource via HTML form.
Thanks
Hi,
I have two resources, Collections and Teams, that I am exposing using express-resource, like this:
var competitions = app.resource("competitions", require('./resources/competitions'));
var teams = app.resource("teams", require('./resources/teams'));
competitions.add(teams);
Teams is then a nested resource within it's parent competition, which is as I wanted it to be. However, I also wanted teams to be accessible as it's own resource. In other words, like this:
/competitions/:id/teams/:id
/teams/:id
So, to get this I changed the code as follows:
var competitions = app.resource("competitions", require('./resources/competitions'));
var teams = app.resource("teams", require('./resources/teams'));
competitions.add(teams);
teams = app.resource("teams", require('./resources/teams'));
I don't know if this is the way it is supposed to be set up, but this actually works. There is only one small problem. I have an auto-load method setup for the team-resource, and when I access the /teams/:id resource it actually ends up calling the auto-load twice. I guess that somehow it fires for both the teams/.id and the competitions/:id/teams/:id resource?
Any ide on how to avoid this from happening?
Hello,
is there a way to disable automatic pluralisation:
default:
http://localhost:3000/forums/1/3/9
I would prefer:
http://localhost:3000/contact
and not:
http://localhost:3000/contacts
Is that possible without hacking express-resource ?
~Marc
Hello, POST/PUT/DELETE always return Forbidden, GET methods works fine... I tried to use the example
exports.index = (req, res) ->
res.send "forum index"
exports["new"] = (req, res) ->
res.send "new forum"
exports.create = (req, res) ->
res.send "create forum"
exports.show = (req, res) ->
res.send "show forum " + req.params.api
exports.edit = (req, res) ->
res.send "edit forum " + req.params.api
exports.update = (req, res) ->
res.send "update forum " + req.params.api
exports.destroy = (req, res) ->
res.send "destroy forum " + req.params.api
I used api resource name instead of forum for my test
So index
,new
,show
,edit
worked fine but create
,update
,destroy
throw me Forbidden :/
Is there a way to to access the resource name and/or action from inside the request handler? e.g.
// app.js
app.resource('user', require('./users.js'));
// users.js
exports.index = function(req, res) {
var resource = req.resource;
var action = req.action;
res.render(resource + "/" + action);
}
After lingo V. 0.0.5 was released and a fresh install of express-resource last week req.param
is always empty.
Maybe package.json
should fix the version of lingo to 0.0.4.
Hi
I am using
Express -- [email protected] and [email protected] on WinXP
When I require express-resource as require('express-resource')
I get the following error
TypeError: Cannot read property 'methods' of undefined
at Object. (D:\some-folder\node_modules\express-resource\index.js:234:15
)
at Module._compile (module.js:441:26)
at Object..js (module.js:459:10)
at Module.load (module.js:348:31)
at Function._load (module.js:308:12)
at Module.require (module.js:354:17)
at require (module.js:370:17)
at Object. (D:\some-folder\news-rest-server.js:7:15)
at Module._compile (module.js:441:26)
at Object..js (module.js:459:10)
app.del
is being used for the destroy route, but .del
has been deprecated since Express 4.2 - it still exists, but it should be removed. I couldn't tell from the README, but are you intending to explicitly support 4.*?
Happy to make the change, just would like to know your approach.
Hi,
I looked thru express-resource/index.js and wondered what happens if I pass an object as third argument in express.HTTPServer.prototype.resource() function.
Each property in opts is copied to options, which is a local variable, at line 259 in index.js.
However, neither opts nor options are passed to the Resource object. The opts argument seems useless.
The comment above resource() doesn't mention the opts at all.
Is this reserved for future functionality?
Thank you,
Bow
We are using nodejs(v 0.10.29 ) ,express,nginx( version 1.4.6) with mongodb(v 2.6.3) replicaset and getting intermittent 502 bad gateway error. pm2 logs is unable to log error though nginx aerror.log is showing
recv() failed (104: Connection reset by peer) while reading response header from upstream, client: xxx.xxx.xxx.xxx, server: somedomain.com, request: "GET /img/abc.png HTTP/1.1", upstream: "http://127.0.0.1:3000/img/abc.png", host: "domain.com", referrer: "http://domain.com/admin/"
and access.log is saying:
"GET /url/abc.html HTTP/1.1" 502 723 "http://domain.com/admin/" "Mozilla/5.0 (X11; Linux i686) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.117 Safari/537.36"
can anyone guide me with the issue?
this is a tracking ticket for a known issue between HEAD of this and express. running the test suite reveals the issue. tj knows the fix. patch coming!
It would be nice if generating a resource, like below, would create a dynamic helper for the view. This would be super useful for styling specific pages.
app.resource('forums', require('./forum'));
exports.index = function(req, res){
res.send('forum index');
};
// bodyClasses() //=> 'forums index'
exports.new = function(req, res){
res.send('new forum');
};
// bodyClasses() //=> 'forums new'
exports.create = function(req, res){
res.send('create forum');
};
// bodyClasses() //=> 'forums create'
exports.show = function(req, res){
res.send('show forum ' + req.params.id);
};
// bodyClasses() //=> 'forums show'
exports.edit = function(req, res){
res.send('edit forum ' + req.params.id);
};
// bodyClasses() //=> 'forums edit'
exports.update = function(req, res){
res.send('update forum ' + req.params.id);
};
// bodyClasses() //=> 'forums update'
exports.destroy = function(req, res){
res.send('destroy forum ' + req.params.id);
};
// bodyClasses() //=> 'forums destroy'
maybe will good to change 'new' to 'store', because will causing conflict with 'new' keyword?
var orderedActions = [
'index' // GET /
, 'create' // GET /
, 'store' // POST /store
, 'show' // GET /:id
, 'edit' // GET /edit/:id
, 'update' // PUT /:id
, 'patch' // PATCH /:id
, 'destroy' // DEL /:id
];
I have a task nested in a list, and the task ID is dependent on the list. The list is auto-loaded, but I can't get access to the list in the task auto-loader because it doesn't receive the request. Is there a more elegant solution, or can/should we pass req down through the auto-load callback?
I'm authenticating with google and using the e-mail address as user name. For user/show the URL is /user/[email protected]. This will always download a file [email protected], even though it should res.render 'user/show'. How can I prevent that? I tried setting req.format and req.params.format to undefined, but didn't work.
Example map:
thingsResource.map('get', '/:min/:max', thingsRouts.getMinMax)
Min and max is float.
I'm making 'get' request to url: things/12.22/12.45 or things/12.22/12.45/
I expected: req.params.min = 12.22 and req.params.max = 12.45, but i get: req.params.max = 12 and req.params.format = 45 instead
Easy workarouund is just make request like: things/12.22/12.45. <- this point is required.
Sorry for my bad english :)
Hi,
following the sample:
var forums = app.resource('forums', require('./routes/forums'));
I can do:
curl -i localhost:3000/forums
how to put the forums behind a path, say:
curl -i localhost:3000/api/forums
?
possible?
angelo
When does the load function actually add to the request object? For the show route it adds fine, but for index, the property for the singular named route is not there.
It would be nice to be able to set route middleware. Specifically, when mapping the users resource:
var user = app.resource('users', require('./user'));
It would be great to do something like:
var user = app.resource('users', require('./user'), { before: authenticateUser });
which is would be the equivalent of
app.get('/users', authenticateUser, require('./user').index);
I noticed that req.session
is undefined for resources but it works fine for manually added routes using app.get
method
{ lastAccess: 1329521660742,
cookie:
{ path: '/',
httpOnly: true,
_expires: Sat, 18 Feb 2012 03:34:20 GMT,
originalMaxAge: 14400000 },
_csrf: 'B91I4oOdFYhs3h9PNdBsJagE' }
GET /404
response time: 20ms
memory rss: 148.00kb
memory vsize: NaNgb
heap before: 29.63mb / 107.27mb
heap after: 30.42mb / 107.30mb
undefined
GET /
response time: 9ms
memory rss: 0b
memory vsize: NaNgb
heap before: 18.28mb / 52.08mb
heap after: 18.85mb / 52.08mb
/
is set through app.resource
and /404
is set through app.get
In the example /forums/5/threads/12
the 'forum' can be loaded in the auto-loading load method. In the load method for a 'thread' there's no way to access the already loaded 'forum' - which could contain either neccesary information to load the 'thread', or could in fact contain the 'thread'.
At the moment, the load method should look like function (id, callback) {}
I don't see the need for passing the id
value, rather than the request. It only hides the key to the param containing the id. I realise that changing the arguments for this would break existing implementations and add a line of code to these implementations, e.g. var id = req.params.forum;
, but I can't see another way.
Currently HTTP requests to a resource that specify a method unimplemented by that resource return a 404 Not Found status code. That is misleading, since the resource can be found—it just doesn't allow that method. It should return a 405 Method Not Allowed in this case.
This behavior (or at least the "Cannot [METHOD] [PATH]" message") seems to be coming from the connect lib; it's not clear to me whether it can or should be fixed there, or in express-resource.
I'm currently using express 3.0.0alpha1-pre (ok, i know it's alpha) and express-resource 0.2.4 (from npm)
As soon as I require express-resource i get this error:
TypeError: Cannot read property 'methods' of undefined
at Object.<anonymous> (/home/myUser/web/myWebsite/private/node_modules/express-resource/index.js:234:15)
That line refers to the following code block
express.router.methods.concat(['del', 'all']).forEach(function(method){
Resource.prototype[method] = function(path, fn){
if ('function' == typeof path
|| 'object' == typeof path) fn = path, path = '';
this.map(method, path, fn);
return this;
}
});
My main question is: Are you planning to make express-resource compatible with Express 3, if so, do you have any ETA?
Thanks for your time =)
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.