Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
tmp*
tmp*
node_modules/
npm-debug.log
43 changes: 43 additions & 0 deletions example/custom_service/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
var feathry = require('../../lib/feathry');
var Proto = require('uberproto');
var users = [
{
id: '1',
name : 'Wayne Campbell',
slogan: 'Party on Garth'
},
{
id: '2',
name : 'Garth Algar',
slogan: 'Party on Wayne'
}
];

var service = {
index : function (params, cb) {
cb(null, users);
},

create: function(data, params, cb) {
console.log(data, params);
users.push(data);
cb(null, data);
},

get : function(id, params, cb) {
for (var user in users){
if (users[user] && users[user].id === id) {
return cb(null, users[user]);
}
}

cb(new Error('User With ID '+ id +' Not Found'));
}
};

Proto.mixin(require('../../lib/mixin/event'), service);

feathry.createServer({ port: 8000 })
.service('users', service)
.provide(feathry.rest())
.start();
10 changes: 10 additions & 0 deletions example/rest_memory/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
var feathry = require('../../lib/feathry');
var Proto = require('uberproto');
var memoryService = feathry.memory();

Proto.mixin(require('../../lib/mixin/event'), memoryService);

feathry.createServer({ port: 8000 })
.service('users', memoryService)
.provide(feathry.rest())
.start();
17 changes: 17 additions & 0 deletions example/rest_memory/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<!DOCTYPE html>
<html>
<head>
<title>Feathry REST Memory Example</title>
</head>
<body>
<h1>Feathry REST Memory Example</h1>

<ul>
<li>Actions</li>
<li><a href="#"></a>GET /users</li>
<li><a href="#"></a>POST /users</li>
<li><a href="#"></a>PUT /users/1</li>
<li><a href="#"></a>DELETE /users/1</li>
</ul>
</body>
</html>
8 changes: 5 additions & 3 deletions lib/errors.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
function createError(name, defaultMessage) {
function Err(message, data) {
function Err(message, data, statusCode) {
this.name = this.type = name;
this.message = message || defaultMessage || "Unknown error";
this.message = message || defaultMessage || "Internal Server Error";
this.data = data;
this.status = statusCode || 500;
}

Err.prototype = Object.create(Error);
Expand All @@ -23,6 +24,7 @@ var ValidationError = exports.ValidationError = function Err(message, data) {
this.name = this.type = 'ValidationError';
this.message = message;
this.data = data;
}
this.status = 400;
};

ValidationError.prototype = Object.create(Error);
11 changes: 10 additions & 1 deletion lib/feathry.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
var server = require('./server');
var _ = require('underscore');
var providers = require('./providers');
var services = require('./services');

exports.errors = require('./errors');
exports.Server = server.Server;
Expand All @@ -9,5 +10,13 @@ exports.createServer = server.createServer;
_.each(providers, function(Provider, name) {
exports[name] = function(options) {
return Provider.create(options);
}
};
});

_.each(services, function(Service, name) {
exports[name] = function(options) {
// TODO: Fix this! Can't call create because UberProto
// calls create which we are overriding in our service
return Service.create(options);
};
});
2 changes: 1 addition & 1 deletion lib/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,4 @@ var Server = Proto.extend({
exports.Server = Server;
exports.createServer = function (options) {
return Server.create(options);
}
};
6 changes: 6 additions & 0 deletions lib/services/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module.exports = {
memory: require('./memory')
// mongo: require('./mongo'),
// redis: require('./redis'),
// couch: require('./couch'),
}
6 changes: 4 additions & 2 deletions lib/service/memory.js → lib/services/memory.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ var mutators = {
__limit: function (values, param) {
return values.slice(0, param);
}
}
};

module.exports = Proto.extend({
var MemoryService = Proto.extend({
init: function (idField) {
this._id = idField || 'id';
this._uId = 0;
Expand Down Expand Up @@ -68,3 +68,5 @@ module.exports = Proto.extend({
cb('Could not find record with ' + id);
}
});

module.exports = MemoryService;
73 changes: 73 additions & 0 deletions lib/services/mongo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
var Proto = require('uberproto');
var error = require('../errors');
var mongo = require('mongoskin');
var _ = require('underscore');
var mutators = {
__sort: function (values, param) {
return _.sortBy(values, function (item) {
return item[param];
});
},
__order: function (values) {
return values.reverse();
},
__offset: function (values, param) {
return values.slice(param);
},
__limit: function (values, param) {
return values.slice(0, param);
}
};

var MongoService = Proto.extend({
init: function (idField) {
this._id = idField || '_id';
this._uId = 0;
this.store = {};
},

index: function (params, cb) {
var values = _.values(this.store);

_.each(mutators, function(handler, name) {
values = params[name] ? handler(values, params[name]) : values;
});

cb(null, values);
},

get: function (id, params, cb) {
if (id in this.store) {
cb(null, this.store[id]);
return;
}
cb(new error.NotFound('Could not find record', { id: id }));
},

create: function (data, params, cb) {
var id = data[this._id] || this._uId++;
data[this._id] = id;
this.store[id] = data;
cb(null, data);
},

update: function (id, data, cb) {
if (id in this.store) {
this.store[id] = data;
cb(null, store[id]);
return;
}
cb('Could not find record with ' + id);
},

destroy: function (id, params, cb) {
if (id in this.store) {
var deleted = this.store[id];
delete this.store[id];
return cb(null, deleted);
}
cb('Could not find record with ' + id);
}
});

module.exports = MongoService;
1 change: 1 addition & 0 deletions license.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
Copyright (C) 2013 David Luecke daff@neyeon.com
Copyright (C) 2013 Eric Kryski e.kryski@gmail.com

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
7 changes: 6 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,20 @@
"url": "git://github.com/daffl/feathry.git"
},
"author": "David Luecke <daff@neyeon.de> (http://neyeon.com)",
"contributors": [
"Eric Kryski <e.kryski@gmail.com> (http://erickryski.com)"
],
"main": "lib/feathry",
"directories": {
"lib": "lib"
},
"scripts": {
"start": "node example/rest/app.js",
"test": "nodeunit test"
},
"engines": {
"node": "*"
"node": "*",
"npm": "*"
},
"dependencies": {
"uberproto": ">= 1.0.0",
Expand Down
66 changes: 51 additions & 15 deletions readme.md
Original file line number Diff line number Diff line change
@@ -1,29 +1,59 @@
# Feathry

A resource service framework for easily creating REST and SocketIO APIs with NodeJS.
#### An ultra scalable, data oriented framework built for tomorrow's web.

## Get started
The core focus of Feathry is your **data**. We believe that ultimately your app's purpose is to manage data in some fashion and so that's all you should really need to deal with. Managing your data. Feathry provides a deadly simple way of managing your data and allows you to provide this data via REST and SocketIO APIs with NodeJS.

Services are just simple objects that provide certain methods. This makes it easy to initialize a
service that provides a single todo:
## Why Another NodeJS Framework?

We know... Oh God another bloody NodeJS framework! Yes we are also very tired of seeing all these NodeJS frameworks. All the rails clones are getting a bit boring and really aren't taking advantage of the real strengths of NodeJS. We wanted to take a different approach than every other framework we have seen, because we believe that data is core to the web and should be the core focus of web applications.

We also think that your data resources can and should be encapsulated in such a way that they can be ultra scalable and self contained. The MVC pattern works well but it is becoming antiquated in today's web. Frankly you don't need it and they tend to become bloated.

With that being said there are some amazing frameworks already out there and we wanted to leverage the ideas that have been put into them, which is why Feathry is built on top of [Express](http://expressjs.com) and is inspired in part by [Flatiron](http://flatironjs.org) and [Derby](http://derbyjs.com).


## Key Concepts

At the core to Feathry are 3 simple but important concepts, **Providers**, **Services** and **Mixins**.

A **Provider** is simply a module that *provides* your data to clients (ie. via REST or Web Sockets).

A **Service** is a module that is used by the provider to actually manage the data (ie. a database adapter)

A **Mixin** is like a utility that you can use to improve your service (ie. validation or authentication)

*TODO: Maybe think of a different name for service*


## Getting Started is Easy

Like we said, services are just simple modules that expose certain methods to the providers. This makes it easy to initialize a service that say... provides a single TODO:

```js
var feathry = require('feathry');

var todoService = {
get: function(name, params, callback) {
callback(null, {
id: name,
description: "You have to do " + name + "!"
});
}
};

feathry.createServer({ port: 8000 })
.service('todo', {
get: function(name, params, callback) {
callback(null, {
id: name,
description: "You have to do " + name + "!"
});
}
})
.service('todo', todoService)
.provide(feathry.rest())
.provide(feathry.socketio())
.start();
```

That's all there really is to building an app with Feathry... Providers, Services, and Mixins!


## Built In Providers

### REST

You can access the REST service by going to `http://localhost:8000/todo/dishes` in your browser
Expand All @@ -36,7 +66,7 @@ and will see:

### SocketIO

Since you added it as a provider, you can also connect to your service via SocketIO.
Since, in the above example, you added it as a provider, you can also connect to your service via SocketIO.
Create an HTML page and insert the following code to see the response data logged on the console:

```html
Expand Down Expand Up @@ -79,9 +109,15 @@ All callbacks follow the `function(error, data)` NodeJS convention.

### setup(server)

## Built in services
## Built In Services

### Memory

### MongoDB (TODO)

### Redis (TODO)

## Service mixins
## Built In Service Mixins

### Events

Expand Down
2 changes: 1 addition & 1 deletion test/service/memory.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ var assert = require('assert');
var MemoryService = require('../../lib/service/memory');
var Proto = require('uberproto');

describe('Memory service', function () {
describe('Memory Service', function () {
it('create', function (done) {
var service = Proto.create.call(MemoryService);
service.create({
Expand Down
Loading