Hi, hi @SkinnyGeek1010. Just wrapped up another project, and wanted to give this some proper thought before replying. Sorry for the delay.
Soā¦ curiously interesting. Wouldnāt have necessarily occurred to me to write the REST API in a functional paradigm; but I donāt see why this couldnāt work splendidly. For high volume throughput, I suspect this could work particularly well, without worrying about the garbage collector keeping up. So, a few thoughts and a basic code review:
Namespacing
Functional programming doesnāt mean we need to ditch namespaces. My hunch is that the general approach will be made more clear by introducing a bit of namespace. Iām just making up some names up, but it could look something like this:
RestApi.scope('/v1', function() {
// resource saves boilerplate using conventions
this.resources('/users', UserController, {except: 'delete'})
this.resources('/comments', CommentController)
this.resources('/posts', PostController)
// or manually add routes
Endpoint.get('/items/', ItemController, 'index')
Endpoint.post('/items', ItemController, 'create')
Endpoint.get('/items/:id', ItemController, 'show')
Endpoint.delete_('/items/:id', ItemController, 'delete')
Endpoint.put('/items/:id', ItemController, 'update')
})
Middleware
Very intriguing about the middleware so far. Any chance you could take a look at oauth2orize and make any recommendations on how to get started with an integration? Not asking for a complete implementation. But how difficult would it be to register a grant and some endpoints with your approach? Something like this?
var server = oauth2orize.createServer();
RestApi.applyMiddleware(
acceptJSON(),
authenticateJWT({except: 'post.user'}),
server.grant(oauth2orize.grant.code(function(client, redirectURI, user, ares, done) {
var code = utils.uid(16);
var ac = new AuthorizationCode(code, client.id, redirectURI, user.id, ares.scope);
ac.save(function(err) {
if (err) { return done(err); }
return done(null, code);
});
}));
);
Isomorphism & Model,View,Controller
Soā¦ this is a pretty standard server-side MVC approach. Weāre in agreement with the controller being a controller; but the Models and Views will confuse some people accustomed to client-side MVC paradigms.
const {sendJson, putStatus} = Restful;
const {Item} from Models;
const {ItemView} from Views;
ItemController = { ... }
So, the question is whether this ought to be isomorphic. Should it be available on the client? If so, itās going to run into the client-side MVC paradigm, which involves ViewPort, device media states, the CSS subsystem, etc. etc. View layer functionality that this paradigm doesnāt necessary take into account. (Not to mention the HTML subsystem and the Document Object Model.) If itās only going to be server-side, thenā¦ yeah, maybe it can make due as-is.
Personally, Iād recommend just scrubbing any reference to Model, View, and Controller, since those terms have a half-dozen different interpretations and are therefore somewhat meaningless and a general cause of confusion.
Other Thoughts on Functional Programming
So, are you familiar with monads yets? The biggest return-on-investment with adding functional programming to javascript apps that weāve found is when there are clear computational pipelines that need to be created. Generally, weāve found them with data visualizations and validation testing. We use long method chain pipelines in both our D3 graphs and our Nightwatch test scripts, and they create magic at runtime.
So whatās the computational pipeline thatās involved with the Rest interface? Thatās the $64,000 question here. Iām not necessarily suggesting that you create a method chaining pattern. But I also kind of am. Itās sort of the logic culmination of this approach. So what would that look like? What does a RestApi method chain look like? What syntactical verbs are used to create a coherent API syntax?
Iām just tossing some ideas out here, but I could imagine a method chaining pattern that connects with the Mongo collections could be extremely useful:
// example 1
Endpoint.route('/items/:id').collection('Items').get();
// example 2
Endpoint.route('/item/:itemId').collection('Items').findOne({_id:itemId).get();
// example 3
Endpoint.route('/item/:itemId').collection('Items').get(function(){
return Items.findOne({_id: this.params.itemId});
});
// example 4
Endpoint.route('/item/mapreduce').collection('Items').find().fetch().get();
// example 5
Endpoint.route('/item/mapreduce').collection('Items').find().map().reduce().get();
Just some thoughtsā¦