Services

Services are the heart of every Feathers application. You probably remember the service we created in the getting started chapter to create and find messages. In this chapter we will dive more into services and update the existing user service in our chat application to include an avatar image.

Feathers services

In general, a service is an object or instance of a class that implements certain methods. Services provide a uniform, protocol independent interface for how to interact with any kind of data like:

  • Reading and/or writing from a database
  • Interacting with the file system
  • Call another API
  • Call other services like
    • Sending an email
    • Processing a payment
    • Returning the current weather for a location, etc.

Protocol independent means that to a Feathers service it does not matter if it has been called through a REST API, websockets, internally in our application or any other way.

Service methods

Service methods are CRUD methods that a service can implement. Feathers service methods are:

  • find - Find all data (potentially matching a query)
  • get - Get a single data entry by its unique identifier
  • create - Create new data
  • update - Update an existing data entry by completely replacing it
  • patch - Update one or more data entries by merging with the new data
  • remove - Remove one or more existing data entries

Below is an example of Feathers service interface as a class and a normal object:

    The parameters for service methods are:

    • id - The unique identifier for the data
    • data - The data sent by the user (for creating, updating and patching)
    • params (optional) - Additional parameters, for example the authenticated user or the query

    Note: A service does not have to implement all those methods but must have at least one.

    Pro tip: For more information about service, service methods and parameters see the Service API documentation.

    When used as a REST API, incoming requests get mapped automatically to their corresponding service method like this:

    Service method HTTP method Path
    service.find({ query: {} }) GET /messages
    service.find({ query: { unread: true } }) GET /messages?unread=true
    service.get(1) GET /messages/1
    service.create(body) POST /messages
    service.update(1, body) PUT /messages/1
    service.patch(1, body) PATCH /messages/1
    service.remove(1) DELETE /messages/1

    Registering services

    As we have seen, a service can be registered on the Feathers application by calling app.use(name, service) with a name and the service instance:

    const app = feathers();
    
    // Register the message service on the Feathers application
    app.use('messages', new MessageService());
    

    To get the service object and use the service methods (and events) we can use app.service(name):

    const messageService = app.service('messages');
    const messages = await messageService.find();
    

    Service events

    A registered service will automatically become a NodeJS EventEmitter that sends events with the new data when a service method that modifies data (create, update, patch and remove) returns. Events can be listened to with app.service('messages').on('eventName', data => {}). Here is a list of the service methods and their corresponding events:

    Service method Service event
    service.create() service.on('created')
    service.update() service.on('updated')
    service.patch() service.on('patched')
    service.remove() service.on('removed')

    This is how Feathers does real-time and how we updated the messages automatically by listening to app.service('messages').on('created').

    Database adapters

    Now that we have all those service methods we could go ahead and implement any kind of custom logic using any backend. Very often, that means creating, reading, updating and removing data from a database.

    Writing all that code yourself for every service is pretty repetitive and cumbersome though which is why Feathers has a collection of pre-built services for different databases. They offer most of the basic functionality and can always be fully customized (as we will see in a bit). Feathers database adapters support a common usage API, pagination and querying syntax for many popular databases and NodeJS ORMs:

    Database Adapter
    In memory feathers-memory, feathers-nedb
    Localstorage, AsyncStorage feathers-localstorage
    Filesystem feathers-nedb
    MongoDB feathers-mongodb, feathers-mongoose
    MySQL, PostgreSQL, MariaDB, SQLite, MSSQL feathers-knex, feathers-sequelize, feathers-objection
    Elasticsearch feathers-elasticsearch

    Pro tip: Each one of the linked adapters has a complete standalone REST API example in their readme.

    In this guide we will use NeDB which is a database that writes to the filesystem and does not require any additional setup. The users service that was created when we generated our application is already using it. In larger applications you probably want to choose something like PostgreSQL or MongoDB but NeDB is great for this guide because it gets us started quickly without having to learn and install a database system.

    Note: NeDB stores its data in our application directory under a data/ folder. It uses a JSON append-only file format. This means that if you look at the database files directly you might see the same entry multiple times but it will always return the correct data.

    Generating a service

    In our newly generated feathers-chat application, we can create database backed services with the following command:

    feathers generate service
    

    For this service we will also use NeDB which we can just confirm by pressing enter. We will use messages as the service name and can confirm all other prompts with the defaults by pressing enter:

    feathers generate service prompts

    This is it, we now have a database backed messages service with authentication enabled.

    Customizing a service

    Feathers has two ways for customizing existing database adapter services. Either by using hooks, which we will look at in the next chapter or by extending the adapter service class. Let's extend our existing users service to add a link to the Gravatar image associated with the user's email address so we can display a user avatar. We will then add that data to the database by calling the original (super.create) method.

      Now we can sign up users with email and password and it will automatically set an avatar image for them. If they have no gravatar, it will return a placeholder image.

      Note: We are keeping githubId from the original data so that we can add a "Login with GitHub" button in the authentication chapter.

      What's next?

      In this chapter we learned about services as Feathers core concept for abstracting data operations. We also saw how a service sends events which we will use later to create real-time applications. After that, we generated a messages service and updated our users service to include an avatar image. Next, we will look at Hooks which is the other key part of how Feathers works.