FeathersJS Basics
Updated: 23 December 2025
Notes from this Coding Garden Series on FeathersJS
Simple Feathers Service
To create a super simple feathers service you can do the following:
Init App
1mkdir feathers-service2cd feathers-service3
4yarn init -y5yarn add @feathersjs/feathersCreate Feathers Instance
Next up, create an app.js file with the following to initialize a feathers app:
app.js
1const feathers = require('@feathersjs/feathers')2const app = feathers()Create Service
You can then create a service, each service needs to have a class which defines what methods are available in the service, for example the MessageService below defines a find and create method:
app.js
1class MessageService {2 constructor() {3 this.messages = []4 }5
6 async find() {7 return this.messages8 }9
10 async create(data) {11 const message = {12 id: this.messages.length,13 text: data.text,14 }15
16 this.messages.push(message)17 return message18 }19}Register Service
We can then register a service by using app.use with a name for the service followed by an instance of the service class:
1app.use('messages', new MessageService())Listen to Events
We can use the app.service('...').on method to add a handler to an event on a service which will allow us to react to the service events:
1app.service('messages').on('created', (message) => {2 console.log('message created')3})Interact with Service
We can interact with a service by referencing a method in a service:
1const main = async () => {2 await app.service('messages').create({3 text: 'hello world',4 })5
6 const messages = await app.service('messages').find()7
8 console.log('messages: ', messages)9}10
11main()Expose as REST and Web Socket
Once we’ve defined a feathers service we can expose it as a REST endpoint as well as a Web Socket automatically using feathers’ express
Install the @feathersjs/express and @feathersjs/socketio packages:
1yarn add @feathersjs/express @feathersjs/socketioThen, we need to configure the app as an express app instead as follows:
1const express = require("@feathersjs/express");2const socketio = require("@feathersjs/socketio";3
4const app = express(feathers());Next, add the middleware for json, urlencoded, and static serving:
1app.use(express.json())2app.use(express.urlencoded({ extended: true }))3app.use(express.static(__dirname))And then, automatically create the epxress and feathers endpoints for our services:
1app.configure(express.rest())2app.configure(socketio())3
4app.use('/messages', new MessageService())Note that the
messagesfrom the previous service definition now becomes/messagesas it’s an endpoint definition now
Lastly, we add the express error handler:
1app.use(express.errorHandler())Now, we will be able to listen to the connection event to trigger something each time a client connects and that will give us access to the connection. We can also add any client that connects to a group so that messages can be broadcast to them:
1// when a user connects2app.on('connection', (connection) => {3 // join them to the everybody channel4 app.channel('everybody').join(connection)5})6
7// publish all changes to the everybody channel8app.publish(() => app.channel('everybody'))Lastly, we start the server:
1app.listen(3030).on('listening', () => {2 console.log('app now listening')3})Now, you can start the application with node app.js and go to http://localhost:3030/messages where you can see the list of messages that are currently in the service
Connect from Browser
Create an index.html file, from here we’ll be connecting to the feathers backend we’ve configured using the feathersjs and socketio clients for the browser:
Which we can then include in the index.html file:
index.html
1<!DOCTYPE html>2<html lang="en">3
4<head>5 <meta charset="UTF-8">6 <meta name="viewport" content="width=device-width, initial-scale=1.0">7 <title>Feathers App</title>8</head>9
10<body>11 <h1>Hello World</h1>12
13 <script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/core-js/2.1.4/core.min.js"></script>14 <script src="//unpkg.com/@feathersjs/client@^4.5.0/dist/feathers.js"></script>15 <script src="//unpkg.com/socket.io-client@^2.3.0/dist/socket.io.js"></script>16</body>17
18</html>Then, in the index.html we can use the following js to subscribe to the socket. We can actually use code that’s almost identical to what we use on the server to interact with the service. An example of a form that allows users to send data to the service and receive updates from the service would look something like this:
index.html
1<!DOCTYPE html>2<html lang="en">3 <head>4 <meta charset="UTF-8" />5 <meta name="viewport" content="width=device-width, initial-scale=1.0" />6 <title>Feathers App</title>7 </head>8
9 <body>10 <h1>Feathers App</h1>11
12 <form onsubmit="sendMessage(event.preventDefault())">13 <input type="text" id="message-text" />14 <button type="submit">Add Message</button>15 </form>16
17 <h2>Messages</h2>18
19 <div id="messages"></div>20
21 <script22 type="text/javascript"23 src="//cdnjs.cloudflare.com/ajax/libs/core-js/2.1.4/core.min.js"24 ></script>25 <script src="//unpkg.com/@feathersjs/client@^4.5.0/dist/feathers.js"></script>26 <script src="//unpkg.com/socket.io-client@^2.3.0/dist/socket.io.js"></script>27
28 <script>29 const createMessage = (message) => {30 document.getElementById('messages').innerHTML += `<div>${message}</div>`31 }32
33 const socket = io('/')34 const app = feathers()35
36 app.configure(feathers.socketio(socket))37
38 const messageService = app.service('messages')39
40 messageService.on('created', (message) => {41 createMessage(message.text)42 })43
44 const sendMessage = async () => {45 const messageInput = document.getElementById('message-text')46
47 await messageService.create({48 text: messageInput.value,49 })50
51 messageInput.value = ''52 }53
54 const main = async () => {55 const messages = await messageService.find()56
57 messages.forEach((m) => createMessage(m.text))58 }59
60 main()61 </script>62 </body>63</html>Init Feathers Application
The previous app that’s been configured is a very simple service. To make a more complete feathers app we will make use of the CLI
To create a new feathers app you will need to use npm or yarn to install the cli
1yarn global add @feathersjs/cliAnd then create an app with:
1mkdir my-feathers-app2cd my-feathers-app3
4feathers generate appBelow are the options I’ve chosen:
1? Do you want to use JavaScript or TypeScript? TypeScript2? Project name app3? Description4? What folder should the source files live in? src5? Which package manager are you using (has to be installed globally)? Yarn6? What type of API are you making? REST, Realtime via Socket.io7? Which testing framework do you prefer? Jest8? This app uses authentication Yes9? What authentication strategies do you want to use? (See API docs for all 11080+ supported oAuth providers) Username + Password (Local)11? What is the name of the user (entity) service? users12? What kind of service is it? NeDB13? What is the database connection string? nedb://../dataConfig
Once created you can find the application config in the config/default.json file. In the config files you can do something like "PORT" as a value which will automatically replace it with an environment variable called PORT, this can apply to any environment variable you want to use in your config file
Entrypoint
The entrypoint to a feathers application is the index.ts file which imports the app as well as some logging config, etc.
Additionally, there’s the app.ts file which pretty much configures an express app for feathers with a lot of the usual configuration settings, body parsers, and middleware
Models and Hooks
The User Model and Class Files specify the default behaviour for the specific user entity. Additionally this also uses hooks to allow us to run certain logic before and after a service is run as well as manage things like authentication
Channels
The channel.ts file is where connections are handled as well as assign users to channels in which they have access to as well as manage what channels get which events published to them
Authentication
The authentication.ts defines an AuthenticationService and configures the auth providers that are available
Configure
The app.configure function is used all over a feathers app. A configure function is a function that takes the app function.
app.configure takes a function that takes the app and is able to configure additional things and create services from the app. The configure function essentially allows us to break out application into smaller parts
Feathers Services
Feathers services are an object or class instance that implements specific methods, they can do things like:
- Read or write from a DB
- Interact with the file system
- Call another API
- Call other services
Service interfaces should implement certain CRUD methods and each service should implement one or more of the following:
| Service | Method | Endpoint Structure | Event Name |
|---|---|---|---|
find | GET | /things?name=john | |
get | GET | /things/1 | |
create | POST | /things | created |
update | PUT | /things/1 | updated |
patch | PATCH | /things/1 | patched |
remove | DELETE | /things/1 | removed |
Incoming requests get mapped to a corresponding rest method
Every service automatically becomes an EventEmitter which means that every time a certain modification action is created then the service will automatically emit the specific event that can then be subscribed to from other parts of the application
Due to the design of services we are able to have each service exposed by Feathers via REST and Web Sockets
Database adapters are just services which have been implemented to work with specific databases automatically
We can generate a service using feathers with:
1feathers generate serviceWhich will then allow you to select the DB to be used for the service as well as a name for it:
1? What kind of service is it? NeDB2? What is the name of the service? messages3? Which path should the service be registered on? /messages4? Does the service require authentication? YesThis will generate a new service in our services directory as well as a model in the models directory
We are also able to modify a service’s class so that it behaves the way we would like it to, for example we can modify the users service class so that it generates an avatar url for each user:
users.class.ts
1export class Users extends Service<User> {2 //eslint-disable-next-line @typescript-eslint/no-unused-vars3 constructor(options: Partial<NedbServiceOptions>, app: Application) {4 super(options)5 }6
7 async create(data: Partial<User>): Promise<User | User[]> {8 const hash = createHash('md5')9 .update(data.email?.toLowerCase() || '')10 .digest('hex')11
12 const avatar = `${gravatarUrl}/${hash}/?${query}`13
14 const userData: Partial<User> = {15 email: data.email,16 password: data.password,17 githubId: data.githubId,18 avatar,19 }20
21 return super.create(userData)22 }23}Hooks
Hooks allow us to make use of reusable components that gets implemented as middleware on all service methods. An example implentation of these are the user hooks which do some authentication as well as output data cleansing:
users.hooks.ts
1export default {2 before: {3 all: [],4 find: [ authenticate('jwt') ],5 get: [ authenticate('jwt') ],6 create: [ hashPassword('password') ],7 update: [ hashPassword('password'), authenticate('jwt') ],8 patch: [ hashPassword('password'), authenticate('jwt') ],9 remove: [ authenticate('jwt') ]10 },11
12 after: {13 all: [14 // Make sure the password field is never sent to the client15 // Always must be the last hook16 protect('password')17 ],18 ...19};We can implemet a hook like createdAt or updatedAt on a service so that we can track date timestamps on an entity and modify data on the context object
In order to generate a hook you can run:
1feathers generate hookAnd then enter the hook name as well as when it should be run:
1? What is the name of the hook? setTimestamp2? What kind of hook should it be? before3? What service(s) should this hook be for (select none to add it yourself)?4 messages5? What methods should the hook be for (select none to add it yourself)? create, updateThen, we can update the hook implementation to match our requirements:s
hooks/set-timestamp.ts
1// Use this hook to manipulate incoming or outgoing data.2// For more information on hooks see: http://docs.feathersjs.com/api/hooks.html3import { Hook, HookContext } from '@feathersjs/feathers'4
5// eslint-disable-next-line @typescript-eslint/no-unused-vars6export default (propName: string): Hook => {7 return async (context: HookContext): Promise<HookContext> => {8 context.data[propName] = new Date()9 return context10 }11}In this case, we would use the above hook with:
1export default {2 before: {3 all: [authenticate('jwt')],4 find: [],5 get: [],6 create: [setTimestamp('createdAt')],7 update: [setTimestamp('updatedAt')],8 ...Service vs Hooks
Services and Hooks are very similar, we will primarily make use of services for functionality that is specific to a service and we will make use of hooks when the functionality is something that can be abstracted and shared with different services