
Research
Security News
Lazarus Strikes npm Again with New Wave of Malicious Packages
The Socket Research Team has discovered six new malicious npm packages linked to North Korea’s Lazarus Group, designed to steal credentials and deploy backdoors.
Feathers is a light weight web application framework that rides on top of Express, one of the most popular web frameworks for NodeJS. It makes it easy to create RESTful web services and real-time applications using SocketIO and several other NodeJS real-time libraries.
If you are not familiar with Express head over to the Express Guides to get an idea. Feathers works the exact same way except that var app = require('express')();
is replaced with var app = require('feathers')()
. The most important concept that Feathers adds to Express middleware is data oriented Services. How services work and the API additional to the available Express API is outlined in the following documentation.
In almost every case you want to exposes your services through a RESTful JSON interface. This can be achieved by calling app.configure(feathers.rest())
.
To set service parameters in a middleware, just attach it to the req.feathers
object which will become the params for any resulting service call:
app.configure(feathers.rest()).use(function(req, res) {
req.feathers.data = 'Hello world';
});
app.use('/todos', {
get: function(name, params, callback) {
console.log(params.data); // -> 'Hello world'
callback(null, {
id: name,
params: params,
description: "You have to do " + name + "!"
});
}
});
The default REST handler is a middleware that formats the data retrieved by the service as JSON. If you would like to configure your own handler
middleware just pass it to feathers.rest(handler)
. For example a middleware that just renders plain text with the todo description (res.data
contains the data returned by the service):
app.configure(feathers.rest(function restFormatter(req, res) {
res.format({
'text/plain': function() {
res.end('The todo is: ' + res.data.description);
}
});
}))
.use('/todo', {
get: function (id, params, callback) {
callback(null, { description: 'You have to do ' + id });
}
});
If you want to add other middleware before the REST handler, simply call app.use(middleware)
before configuring the handler.
To expose services via SocketIO call app.configure(feathers.socketio())
. It is also possible pass a function(io) {}
when initializing the provider where io
is the main SocketIO object. Since Feathers is only using the SocketIO default configuration, this is a good spot to initialize the recommended production settings:
app.configure(feathers.socketio(function(io) {
io.enable('browser client minification'); // send minified client
io.enable('browser client etag'); // apply etag caching logic based on version number
io.enable('browser client gzip'); // gzip the file
// enable all transports (optional if you want flashsocket support, please note that some hosting
// providers do not allow you to create servers that listen on a port different than 80 or their
// default port)
io.set('transports', [
'websocket'
, 'flashsocket'
, 'htmlfile'
, 'xhr-polling'
, 'jsonp-polling'
]);
}));
Note: io.set is deprecated in Socket.IO 1.0. The above configuration will still work but will be replaced with the recommended production configuration for version 1.0 (which isn't available at the moment).
This is also the place to listen to custom events or add authorization:
app.configure(feathers.socketio(function(io) {
io.on('connection', function(socket) {
socket.emit('news', { hello: 'world' });
socket.on('my other event', function (data) {
console.log(data);
});
});
io.use(function (socket, next) {
// Authorize using the /users service
app.lookup('users').find({
username: socket.request.username,
password: socket.request.password
}, next);
});
}));
Similar than the REST middleware, the SocketIO handshakes feathers
property will be extended
for service parameters:
app.configure(feathers.socketio(function(io) {
io.use(function (socket, next) {
socket.feathers.user = { name: 'David' };
next();
});
}));
app.use('todos', {
create: function(data, params, callback) {
// When called via SocketIO:
params.user // -> { name: 'David' }
}
});
Once the server has been started with app.listen()
the SocketIO object is available as app.io
.
Primus is a universal wrapper for real-time frameworks and allows you to transparently use Engine.IO, WebSockets, BrowserChannel, SockJS and Socket.IO. Set it up with feathers.primus(configuration [, fn])
where configuration
is the Primus server configuration and fn
an optional callback with the Primus server instance that can e.g. be used for setting up authorization:
// Set up Primus with SockJS
app.configure(feathers.primus({
transformer: 'sockjs'
}, function(primus) {
// Set up Primus authorization here
primus.authorize(function (req, done) {
var auth;
try { auth = authParser(req.headers['authorization']) }
catch (ex) { return done(ex) }
// Do some async auth check
authCheck(auth, done);
});
}));
In the Browser you can connect like this:
<script type="text/javascript" src="primus/primus.js"></script>
<script type="text/javascript">
var primus = new Primus(url);
primus.on('todos created', function(todo) {
console.log('Someone created a Todo', todo);
});
primus.send('todos::create', { description: 'Do something' }, {}, function() {
primus.send('todos::find', {}, function(error, todos) {
console.log(todos);
});
});
</script>
Just like REST and SocketIO, the Primus request object can be extended with a feathers
parameter during authorization which will extend the params
for any service request:
app.configure(feathers.primus({
transformer: 'sockjs'
}, function(primus) {
// Set up Primus authorization here
primus.authorize(function (req, done) {
req.feathers = {
user: { name: 'David' }
}
done();
});
}));
As mentioned, the basic Feathers functionality is fully compatible with Express. The key concept added to that of middleware is *service objects. A service can be any JavaScript object that offers one or more of the find
, get
, create
, update
, remove
and setup
service methods with the following signatures:
var myService = {
find: function(params, callback) {},
get: function(id, params, callback) {},
create: function(data, params, callback) {},
update: function(id, data, params, callback) {},
patch: function(id, data, params, callback) {},
remove: function(id, params, callback) {},
setup: function(app) {}
}
And can be used like any other Express middleware app.use('/my-service', myService)
.
All service callbacks follow the function(error, data)
NodeJS convention. params
can contain any additional parameters, for example the currently authenticated user. REST service calls set params.query
with the query parameters (e.g. a query string like ?status=active&type=user
becomes { query: { status: "active", type: "user" } }
), socket call parameters will also be passed as params.query
.
It is also possible to return a Promise object from a service instead of using the callback, for example using Q:
var Q = require('q');
var todos = {
get: function(id) {
var dfd = Q.defer();
setTimeout(function() {
dfd.resolve({
id: id,
description: 'You have to do ' + id
});
}, 500);
return dfd.promise;
}
}
find(params, callback)
retrieves a list of all resources from the service. SocketIO parameters will be passed as params.query
to the service.
REST
GET todo?status=completed&user=10
SocketIO
socket.emit('todo::find', {
status: 'completed'
user: 10
}, function(error, data) {
});
Will call .create with
params
{ query: { status: 'completed', user: 10 } }
get(id, params, callback)
retrieves a single resource with the given id
from the service.
REST
GET todo/1
SocketIO
socket.emit('todo::get', 1, {}, function(error, data) {
});
create(data, params, callback)
creates a new resource with data
. The callback should be called with the newly
created resource data.
REST
POST todo
{ "description": "I really have to iron" }
By default the body can be eihter JSON or form encoded as long as the content type is set accordingly.
SocketIO
socket.emit('todo::create', {
description: 'I really have to iron'
}, {}, function(error, data) {
});
update(id, data, params, callback)
updates the resource identified by id
using data
. The callback should
be called with the updated resource data.
REST
PUT todo/2
{ "description": "I really have to do laundry" }
SocketIO
socket.emit('todo::update', 2, {
description: 'I really have to do laundry'
}, {}, function(error, data) {
// data -> { id: 2, description: "I really have to do laundry" }
});
patch(id, data, params, callback)
patches the resource identified by id
using data
. The callback should be called with the updated resource data. Implement patch
additionally to update
if you want to separate between partial and full updates and support the PATCH
HTTP method.
REST
PATCH todo/2
{ "description": "I really have to do laundry" }
SocketIO
socket.emit('todo::patch', 2, {
description: 'I really have to do laundry'
}, {}, function(error, data) {
// data -> { id: 2, description: "I really have to do laundry" }
});
remove(id, params, callback)
removes the resource with id
. The callback should be called with the removed resource.
REST
DELETE todo/2
SocketIO
socket.emit('todo::remove', 2, {}, function(error, data) {
});
setup(app, path)
initializes the service passing an instance of the Feathers application and the path it has been registered on. The SocketIO server is available via app.io
. setup
is a great way to connect services:
var todoService = {
get: function(name, params, callback) {
callback(null, {
id: name,
description: 'You have to ' + name + '!'
});
}
};
var myService = {
setup: function(app) {
this.todo = app.lookup('todo');
},
get: function(name, params, callback) {
this.todo.get('take out trash', {}, function(error, todo) {
callback(error, {
name: name,
todo: todo
});
});
}
}
feathers()
.use('todo', todoService)
.use('my', myService)
.listen(8000);
You can see the combination when going to http://localhost:8000/my/test
.
Pro tip:
Bind the apps lookup
method to your service to always look your services up dynamically:
var myService = {
setup: function(app) {
this.lookup = app.lookup.bind(app);
},
get: function(name, params, callback) {
this.lookup('todos').get('take out trash', {}, function(error, todo) {
callback(null, {
name: name,
todo: todo
});
});
}
}
Any registered service will be automatically turned into an event emitter that emits events when a resource has changed, that is a create
, update
or remove
service call returned successfully. It is therefore possible to bind to the below events via app.lookup(servicename).on()
and, if enabled, all events will also broadcast to all connected SocketIO clients in the form of <servicepath> <eventname>
. Note that the service path will always be stripped of leading and trailing slashes regardless of how it has been registered (e.g. /my/service/
will become my/service
).
The created
event will be published with the callback data when a service create
calls back successfully.
app.use('/todos', {
create: function(data, params, callback) {
callback(null, data);
}
});
app.lookup('/todos').on('created', function(todo) {
console.log('Created todo', todo);
});
app.lookup('/todos').create({
description: 'We have to do something!'
}, {}, function(error, callback) {
// ...
});
app.listen(8000);
SocketIO
<script src="http://localhost:8000/socket.io/socket.io.js"></script>
<script>
var socket = io.connect('http://localhost:8000/');
socket.on('todos created', function(todo) {
console.log('Got a new Todo!', todo);
});
</script>
The updated
and patched
events will be published with the callback data when a service update
or patch
method calls back successfully.
app.use('/my/todos/', {
update: function(id, data, params, callback) {
callback(null, data);
}
});
app.listen(8000);
SocketIO
<script src="http://localhost:8000/socket.io/socket.io.js"></script>
<script>
var socket = io.connect('http://localhost:8000/');
socket.on('my/todos updated', function(todo) {
console.log('Got an updated Todo!', todo);
});
socket.emit('my/todos::update', 1, {
description: 'Updated description'
}, {}, function(error, callback) {
// Do something here
});
</script>
The removed
event will be published with the callback data when a service remove
calls back successfully.
app.use('/todos', {
remove: function(id, params, callback) {
callback(null, { id: id });
}
});
app.lookup('/todos').remove(1, {}, function(error, callback) {
// ...
});
app.listen(8000);
SocketIO
<script src="http://localhost:8000/socket.io/socket.io.js"></script>
<script>
var socket = io.connect('http://localhost:8000/');
socket.on('todos removed', function(todo) {
// Remove element showing the Todo from the page
$('#todo-' + todo.id).remove();
});
</script>
By default all service events will be dispatched to all connected clients.
In many cases you probably want to be able to only dispatch events for certain clients.
This can be done by implementing the created
, updated
, patched
and removed
methods as function(data, params, callback) {}
with params
being the parameters set when the client connected, in SocketIO when authorizing and setting handshake.feathers
and Primus with req.feathers
.
var myService = {
created: function(data, params, callback) {},
updated: function(data, params, callback) {},
patched: function(data, params, callback) {},
removed: function(data, params, callback) {}
}
The event dispatching service methods will be run for every connected client. Calling the callback with data (that you also may modify) will dispatch the according event. Callling back with a falsy value will prevent the event being dispatched to this client.
The following example only dispatches the Todo updated
event if the authorized user belongs to the same company:
app.configure(feathers.socketio(function(io) {
io.use(function (socket, callback) {
// Authorize using the /users service
app.lookup('users').find({
username: handshake.username,
password: handshake.password
}, function(error, user) {
if(!error || !user) {
return callback(error, false);
}
socket.feathers = {
user: user
};
callback(null, true);
});
});
}));
app.use('todos', {
update: function(id, data, params, callback) {
// Update
callback(null, data);
},
updated: function(todo, params, callback) {
// params === handshake.feathers
if(todo.companyId === params.user.companyId) {
// Dispatch the todo data to this client
return callback(null, todo);
}
// Call back with a falsy value to prevent dispatching
callback(null, false);
}
});
On the client:
socket.on('todo updated', function(data) {
// The client will only get this event
// if authorized and in the same company
});
app.listen([port])
starts the application on the given port. It will first call the original Express app.listen([port]), then run app.setup(server)
(see below) with the server object and then return the server object.
app.setup(server)
is used initialize all services by calling each services .setup(app, path)
method (if available).
It will also use the server
instance passed (e.g. through http.createServer
) to set up SocketIO (if enabled) and any other provider that might require the server instance.
Normally app.setup
will be called automatically when starting the application via app.listen([port])
but there are cases when you need to initialize the server separately:
HTTPS
With your Feathers application initialized it is easy to set up an HTTPS REST and SocketIO server:
app.configure(feathers.socketio()).use('/todos', todoService);
var https = require('https');
var server = https.createServer({
key: fs.readFileSync('privatekey.pem'),
cert: fs.readFileSync('certificate.pem')
}, app).listen(443);
// Call app.setup to initialize all services and SocketIO
app.setup(server);
Virtual Hosts
You can use feathers.vhost
(which is the same as Express and Connect .vhost) to run your Feathers app on a virtual host:
app.use('/todos', todoService);
var host = feathers().use(feathers.vhost('foo.com', app));
var server = host.listen(8080);
// Here we need to call app.setup because .listen on our virtal hosted
// app is never called
app.setup(server);
app.lookup(path)
returns the wrapped service object for the given path. Note that Feathers internally creates a new object from each registered service. This means that the object returned by lookup
will provide the same methods and functionality as the original service but also functionality added by Feathers (most notably it is possible to listen to service events). path
can be the service name with or without leading and trailing slashes.
app.use('/my/todos', {
create: function(data, params, callback) {
callback(null, data);
}
});
var todoService = app.lookup('my/todos');
// todoService is an event emitter
todoService.on('created', function(todo) {
console.log('Created todo', todo);
});
app.use([path], service)
works just like Express app.use([path], middleware) but additionally allows to register a service object (an object which at least provides one of the service methods as outlined in the Services section) instead of the middleware function. Note that REST services are registered in the same order as any other middleware so the below example will allow the /todos
service only to Passport authenticated users.
// Serve public folder for everybody
app.use(feathers.static(__dirname + '/public');
// Make sure that everything else only works with authentication
app.use(function(req,res,next){
if(req.isAuthenticated()){
next();
} else {
// 401 Not Authorized
next(new Error(401));
}
});
// Add a service.
app.use('/todos', {
get: function(name, params, callback) {
callback(null, {
id: name,
description: "You have to do " + name + "!"
});
}
});
app.service([path], service)
is what is called internally by app.use([path], service)
if a service object is being passed. Use it instead of app.use([path], service)
if you want to be more explicit that you are registering a service. app.service
does not provide the Express app.use
functionality and doesn't check the service object for valid methods.
We know! Oh God another NodeJS framework! We really didn't want to add another name to the long list of NodeJS web frameworks but also wanted to explore a different approach than any other framework we have seen. We strongly believe that data is the core of the web and should be the focus of web applications.
We also think that your data resources can and should be encapsulated in such a way that they can be scalable, easily testable and self contained. The classic web MVC pattern used to work well but is becoming antiquated in today's web.
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 Feathers is built on top of Express and is inspired in part by Sails, Flatiron and Derby.
0.4.0
patch
support (#47)0.3.2
0.3.1
0.3.0
app.setup(server)
to support HTTPS (and other functionality that requires a custom server) (#33)0.2.0
req.feathers
in REST provider to set service parameters0.1.0
0.0.x
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 in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
FAQs
Build Better APIs, Faster than Ever.
We found that feathers demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 2 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Security News
The Socket Research Team has discovered six new malicious npm packages linked to North Korea’s Lazarus Group, designed to steal credentials and deploy backdoors.
Security News
Socket CEO Feross Aboukhadijeh discusses the open web, open source security, and how Socket tackles software supply chain attacks on The Pair Program podcast.
Security News
Opengrep continues building momentum with the alpha release of its Playground tool, demonstrating the project's rapid evolution just two months after its initial launch.