What is Feathers?
Feathers is the simple and lightweight framework that allows you to quickly build APIs (both RESTful and realtime) as well as connect to them using frontend JS libraries. The most important point in its philosophy is DRY (Don’t repeat yourself) approach. The goal of Feathers is to handle all the repetitive operations, letting you to implement only the things that are specific to your app. It achieves this goal by:
– providing CLI for generating project structure, services, model files, hooks (more on hooks later)… – so you don’t have to create all the standard files/folders or write boilerplate code yourself
– using the uniform service interface (similar to REST) – it allows Feathers to implement things like communication protocol, error handling, database storage and others in a standard way
Last thing to mention before we create our first API is that Feathers doesn’t implement everything from scratch. It uses popular and battle-tested solutions under the hood including Express, Yeoman, Socket.io, passport, mongoose or sequelize. This makes Feathers stable and compatible with many JS libraries.
Creating the project
Before we create (generate) the project structure let’s briefly talk about the app itself. In this tutorial we’ll implement the API that can be used for tasks management. We’ll be able to add, edit, view or delete our tasks. To keep it simple we won’t assign tasks to the users (even though it’s really quick to add users and authentication in Feathers).
OK, so to the point – let’s start by opening our terminal/console and installing Feathers CLI (assuming you already have npm installed).
npm install -g @feathersjs/cli
We can generate the basic project structure using CLI now.
feathers generate app
Feathers will ask us about some project details then. Let’s set the name to todomanager and leave the defaults for others.
? Project name todomanager ? Description ? What folder should the source files live in? src ? Which package manager are you using (has to be installed globally)? npm ? What type of API are you making? REST, Realtime via Socket.io
After automatically installing required packages we have the basic project ready to go. Let’s check it by running
npm start and opening
http://localhost:3030 in a web browser. Feathers welcome screen should appear.
Also let’s quickly examine the project structure. We have the following files and folders:
– package.json – regular Node package configuration, most interesting entries here are
test scripts that allows us to (surprise) start and run test for our app respectively
– node_modules – installed node packages
– public – public files served by our app, currently including the welcome page you’ve seen before as well as favicon file
– config – app configuration settings, there are two files here: default.json that stores default configuration and production.json that overrides some values fron default.json when on production environment
– src – the whole app source code is here including services, models, middleware and hooks
– test – tests for our app
Creating our first service
To create the service we will use another generator like this:
feathers generate service
HINT: To see the full list of generators just run
Now we are to choose how our service is about to store data. Most popular choices would be Mongoose (for MongoDB) or Sequelize for SQL databases, but for this tutorial we’ll use NeDB that is really simple and convinient (and its API is a subset of MongoDB’s). It stores data in .db files (inside data folder) as json documents – one for each row. It’s simple to edit and doesn’t require former db server installation.
After choosing NeDB we can set the name of the service to tasks and leave the default path value as well as db connection string.
? What kind of service is it? NeDB ? What is the name of the service? tasks ? Which path should the service be registered on? /tasks ? What is the database connection string? nedb://../data
Our service file is generated along with others: task model, hooks and tests. You can take a look at tasks.service.js file, but it’s mostly boilerplate code that you won’t need to edit in most cases (as you will keep most of your app logic in hooks). However it’s good to know that you can override service default behavior if necessary.
One last think I’d like to mention here is that it is possible to generate a custom service that doesn’t use data storage (you may have noticed such an option while generating tasks service). It is useful for creating services that aggregate data from other ones or doing some stuff not related to data storage at all.
Using the service
Now after creating our service we can play with it for a while using tool like Postman for making RESTful API calls.
There are a few methods available in each service in Feathers (with corresponding HTTP actions and paths in parentheses):
– find (GET /tasks) – finds all the documents matching the query (optional), also supports sorting and pagination
– get (GET /tasks/:id) – finds a single document by id
– create (POST /tasks) – creates a new document
– update (PUT /tasks/:id) – updates the whole document by overwriting it with provided one (missing fields will be cleared)
– patch (PATCH /tasks/:id) – updates only the fields provided (missing fields will remain untouched)
– remove (DELETE /tasks/:id) – removes the document
Let’s start with adding a few tasks with just name like below:
Feathers will respond with newly created document including id.
HINT: Notice your tasks are being added to NeDB file (data/tasks.db).
Feel free to play around with other actions like editing or deleting tasks, but we’re going to take a closer look at find method that gives us a few handy features.
First, let’s call
http://localhost:3030/tasks using GET method (you can use either Postman or your browser). As a response you’ll receive JSON document similar to this one:
We have all the documents here as well as some additional information: total number of documents, limit of documents returned at once and number of skipped documents. The last two are useful for paging – default page limit can be set in configuration (config/default.json) or in the url.
So for example calling:
http://localhost:3030/tasks?$limit=2&$skip=1 will result in:
It’s also possible to find documents by its field value:
or to sort the results:
More on available queries here
As you can see we are already able to perform a lot of common operations and we haven’t written a single line of code yet 🙂 But now it’s time to dig deeper into Feathers. Let’s talk about…
Hooks are the way to add custom logic to generic auto-generated services. And they do it in a way that doesn’t compromise DRY rule. So what are hooks? They are functions called before or after our service method or in case of error. For example we can use hook to authenticate user before executing service method, to validate incoming data, to modify data returned by our service method, to log errors and so on.
Let’s say we’d like to set updatedAt field of our task to current date and time every time it’s updated. To do that we will use hooks. We could implement our own hook (and we’ll do that in a moment for some other case), but now we can simply use one of the hooks provided in feathers-hooks-common package. First let’s install it:
npm install --save feathers-hooks-common
Next we need to edit the file defining hooks for our tasks service (services/tasks/tasks.hooks.js). This is how it looks like right now:
We have objects for hooks called before and after actions as well as in case of error. Each object contains arrays of hooks assigned to particular service actions (like find or create) as well as field called all that contains array of hooks executed for each action.
The hook that we’re going to use is called setNow and it takes the name (or names) of fields that should be set to current date and time as parameter.
First we need to import it from feathers-hooks-common package. Then we can add it in every place that modifies our task. Our code will look like this after those changes:
If you play around with adding and modifying tasks you will notice the new field automatically added to all documents:
HINT: Remember to restart your development server after making changes to the code.
There are many more standard hooks that you can find here, including ones that can be used for modifying data, caching, validation, conditional hooks execution and more.
Creating custom hook
Finally we’re going to create our own hook. It will make sure that the name of the task will not be empty. To add the hook file and it’s basic structure we’ll use generator again. We’ll call it validateName and select I will add it myself, because we’ll add it in a few custom places later.
feathers g hook (
g here is short for
? What is the name of the hook? validateName ? What kind of hook should it be? I will add it myself
The validate-name.js file in hooks folder will be generated:
As you can see it exports a function. This function is called when defining a hook (as we did with common setNow hook before) and it returns another function that will be called once the hook is executed (i.e. before/after action or in case of error). Right now this generated function only returns context wrapped in promise and we’re going to replace it with our own code now.
We need three steps:
– get name value somehow
– check if it’s missing or empty
– throw an error if so
First we have to find name value that is send in the request to the API. It’s available – along with many other useful things – inside context variable or to be more precise in its data field (more on hook context here).
npm install --save feathers-errors
We removed default return statement, because it’s ok for hook function to return undefined too (more info here)
Finally we need to assign our custom hook to appropriate actions:
Adding the new task without name will result in an error now.
OK, seems to be working fine for creating tasks, but what about patch method? It’s perfectly fine to send request without name when using patch, however it can’t be empty if provided. We could create another hook for this slightly different behavior, but it will result in repeating the code. Instead we’re about to use hook parameter to indicate that missing name field is fine. First we’ll add the parameter and associated if condition:
Then we’ll set it to true for patch method only:
Now patch method will work fine if name is not provided, but will still return error if name field is present, but empty.
We only scratched the surface here, but having basic understanding of Feathers philosophy and conventions it’s pretty easy to develop your app further (great documentation is a big help too). Feathers itself provides ready to use solutions for authentication (including JWT and OAUTH), support for SQL and NoSQL databases, validation as well as the library that helps to connect with Feathers from a frontend app. There is also a lot of external libraries created for Feathers making easy integration with tools and frameworks like: Angular/RxJS, React/Redux, VueJS, Mailgun/Sendgrid. And last but not least, as Feathers is based on Express you can use any library or middleware made for it too. All of that makes Feathers quick and simple in typical use cases, but also really flexible when necessary.