REST & realtime API (two way communication)
Feather mainly deals with
Services - easy to start up with REST style services
event
hooks (callback hook on services)
channels (event distribution)
Transport - REST & two way real time transport (socket or Primus)
Authentication
Database
Client
integrate with service
NodeJS 6 up, knowledge of Express
From scratch (as a node application)
npm init --yes
npm install @feathersjs/feathers --save
Import feathers module(s) & get application
import feathers: const feathers = require('@feathersjs/feathers');
initialize application: const app = feathers();
API reference: https://docs.feathersjs.com/api/application.html
Get started in page (unpkg : content deliver network for all npm packages) with feathers and own client js
<script type="text/javascript" src="//unpkg.com/@feathersjs/client@^3.0.0/dist/feathers.js"></script>
<script src="client.js"></script>
Run app.js - node app.js
Serve public/
(npm install http-server -g)
http-server public/
API reference https://docs.feathersjs.com/api/services.html
Methods
find - all, from a query maybe
get - get a single entry by id
create
update - replace
patch - update by merging
remove
const myService = {
async find(params) {
return [];
},
async get(id, params) {},
async create(data, params) {},
async update(id, data, params) {},
async patch(id, data, params) {},
async remove(id, params) {}
}
app.use('/my-service', myService); // expose the service at path
Define
app.use('todos', {
async get(name) {
// Return an object in the form of { name, text }
return {
name,
text: `You have to do ${name}`
};
}
});
Use (local)
async function getTodo(name) {
// Get the service we registered above
const service = app.service('todos');
// Call the `get` method with a name
const todo = await service.get(name);
// Log the todo we got back
console.log(todo);
}
Service Event
Registered service automatically become a NodeJS event emitter and send events when
create, update, patch, remove
returns
event name: xx -> xxed
Listen: app.service('path').on('eventName', data => {...})
multiple add - listener called multiple times
returns a reference to the EventEmitter so calls can be chained
Hooks are functions registered
before, after, error
of a service method, transport independent
or, of an application (to all service methods)
before all "before" service specific hooks
after all "after" service specific hooks
after all "error" service specific hooks
Register
app.service('messages').hooks({
before: {
create: async context => {
......
}
}
})
const messagesHooks = {
before: {
all: [], // means run before method-specific hooks in all methods
find: [],
get: [],
create: [],
update: [],
patch: [],
remove: [],
},
after: {
all: [],
find: [],
create: [],
update: [],
patch: [],
remove: [],
}
};
app.service('messages').hooks(messagesHooks);
register application hook
app.hooks({
error: async context => {
console.error(`Error in '${context.path}' service method '${context.method}'`, context.error.stack);
}
});
Context
https://docs.feathersjs.com/guides/basics/hooks.html#hook-context
Use
For validation, throw a Feathers erorr
npm install @feathersjs/errors --save
const { BadRequest } = require('@feathersjs/errors');
const validate = async context => {
const { data } = context;
... // if data not valid... throw new BadRequest('error message');
... // if data valid return context;
};
app.service('messages').hooks({
before: {
create: validate,
update: validate,
patch: validate
}
});
HTTP REST via Express
Socket.io - full two way communication
Primus - alternative to Socket.io
REST
Service method HTTP method Path
.find() GET /messages
.get() GET /messages/1
.create() POST /messages
.update() PUT /messages/1
.patch() PATCH /messages/1
.remove() DELETE /messages/1
npm install @feathersjs/express --save
Feather - express reference: https://docs.feathersjs.com/api/express.html
const feathers = require('@feathersjs/feathers');
const express = require('@feathersjs/express');
// This creates an app that is both, an Express and Feathers app
const app = express(feathers());
// Turn on JSON body parsing for REST services
app.use(express.json())
// Turn on URL-encoded body parsing for REST services
app.use(express.urlencoded({ extended: true }));
// Set up REST transport using Express
app.configure(express.rest());
// Set up an error handler that gives us nicer errors
// ALWAYS has to be the last line before starting server
app.use(express.errorHandler());
// Start the server on port 3030
app.listen(3030);
Using REST
curl 'http://localhost:3030/messages/' -H 'Content-Type: application/json' --data-binary '{ "text": "Hello from the command line!" }'
curl -X "DELETE" http://localhost:3030/messages/1
use Postman to make requests
With bunch of adapters, see https://docs.feathersjs.com/guides/basics/databases.html
Querying: https://docs.feathersjs.com/api/databases/querying.html
Real-time means events (created/updated/patched/removed) pushed to connected clients so they can react. Supported by Socket.io / Primus
Generally recommend using real-time transport whenever possible.
npm install @feathersjs/socketio --save
const feathers = require('@feathersjs/feathers');
const socketio = require('@feathersjs/socketio');
// Create a Feathers application
const app = feathers();
// Configure the Socket.io transport
app.configure(socketio());
// Start the server on port 3030
app.listen(3030);
Channels
Determine which real-time events to which client. On connection, join connection to channel, on event, publish event.
// On any real-time connection, add it to the `everybody` channel
app.on('connection', connection => app.channel('everybody').join(connection));
// Publish all events to the `everybody` channel
app.publish(() => app.channel('everybody'));
Browser client
creating socket, listen to event, and call service through socket
/* global io */
// Create a websocket connecting to our Feathers server
const socket = io('http://localhost:3030');
// Listen to new messages being created
socket.on('messages created', message =>
console.log('Someone created a message', message)
);
socket.emit('create', 'messages', {
text: 'Hello from socket'
}, (error, result) => {
if (error) throw error
socket.emit('find', 'messages', (error, messageList) => {
if (error) throw error
console.log('Current messages', messageList);
});
});
Real time client
// Create a websocket connecting to our Feathers server
const socket = io('http://localhost:3030');
// Create a Feathers application
const app = feathers();
// Configure Socket.io client services to use that socket
app.configure(feathers.socketio(socket));
// listen to an event from server
app.service('messages').on('created', message => {
console.log('Someone created a message', message);
});
// call a service on server
async function createAndList() {
await app.service('messages').create({
text: 'Hello from Feathers browser client'
});
const messages = await app.service('messages').find();
console.log('Messages', messages);
}
createAndList();
REST client
Can use many Ajax libraries like jQuery, Axios, or "fetch" - built in modern browsers
REST services can only emit real-time events locally. Does not support real time update from server. That means if calling the service from local, event can be emitted and observed. If call service by other client (curl from command line) event won't be received.
To enable cross-domain request, enable Cross-Origin Resource Sharing (CORS) on server (app.js)
// Enable CORS
app.use(function(req, res, next) {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');
next();
});
Client
// Create a Feathers application
const app = feathers();
// Initialize a REST connection
const rest = feathers.rest('http://localhost:3030');
// Configure the REST client to use 'window.fetch'
app.configure(rest.fetch(window.fetch));
// listen on message
app.service('messages').on('created', message => {
console.log('Created a new message locally', message);
});
// call server service
async function createAndList() {
await app.service('messages').create({
text: 'Hello from Feathers browser client'
});
const messages = await app.service('messages').find();
console.log('Messages', messages);
}
createAndList();
Initialize new Feathers application with recommended structure. And
Configure authentication
Generate database backed services
Setup database connection
Generate hooks (with tests)
Adding Express middleware
npm install @feathersjs/cli -g
feathers --version
Configuration functions - organize application configuration in different files
in separate file like "messages.service.js", export the configuration function
module.exports = function(app){ app.use... }
import into app and use
const configureMessages = require('./messages.service.js');
const app = feathers();
app.configure(configureMessages); // pass a callback into app.configure
order is important
Hook functions
in separate file like hooks/set-timestamp.js
module.exports = ( {name}) => { return async context => { context.data[name] = new Date(); return context; }}
hint: {name} - destructuring argument, so pass something with something.name -> name
to use
const setTimestamp = require('./hooks/set-timestamp.js');
app.service('serviceName').hooks({ before: { create: setTimestamp({name: 'createdAt'}...
note: using an options object here to allow more easily add new options