Learn Sails.js - Part 5

von Manuel am 21.09.2025 um 12:00 Uhr

Excellent. The ship is seaworthy, the holds are filling with data, and you know how to steer. But a complex voyage requires more than just a single captain on the bridge shouting orders. It requires a specialist crew. It’s time to hire some experts.


Part 5: Assembling a Specialist Crew (Services)


The Captain’s Briefing (Background and Theory)

Navigator, as your voyages become more ambitious, your Captain’s Log (the CaptainController.js file) will start to get cluttered. Imagine you need to perform a complex calculation, like determining the total value of a captain’s treasure, adjusted for inflation, tides, and the current price of rum.

Where would you put that code?

You could write a huge, messy function directly inside a controller action. But what if you need to perform that same exact calculation somewhere else, like in a different controller or in a script? You’d have to copy and paste the code. This is a cardinal sin in programming, a violation of the DRY (Don’t Repeat Yourself) principle. Copied code is twice as hard to maintain and twice as likely to have bugs.

This is the problem Services are designed to solve.

Services: The Specialist Crew

A Service is a dedicated specialist on your ship. It’s a file that lives in api/services/ and contains a collection of related helper functions. Think of them by their specialty:

A controller’s job is to be the manager. It should be lean and decisive. It receives a request, calls on the appropriate specialists (models and services) to do the heavy lifting, and then decides what response to send. The controller gives the order; the service executes the skill.

Using Services

Once you create a service file like AppraisalService.js, Sails makes it globally available throughout your backend code. You don’t need to require() it. You can simply call its methods from any controller, policy, or even another service by using its global name.

If you have a function called calculateTax in AppraisalService.js, you can call it from anywhere like this: const taxedValue = await AppraisalService.calculateTax(treasure);

This makes your controllers incredibly clean and easy to read. Compare these two styles:

Bloated Controller (Bad):

// In a controller action
const treasure = await Treasure.findOne({ id: req.param('id') });
let taxedValue = treasure.value;
// ... 20 lines of complex tax calculation logic here ...
// ... what if the tax rules change? You have to find this code and change it.
return res.json({ taxedValue: taxedValue });

Clean Controller with Service (Good):

// In a controller action
const treasure = await Treasure.findOne({ id: req.param('id') });
// Call the specialist crew member!
const taxedValue = await AppraisalService.calculateTax(treasure);
return res.json({ taxedValue: taxedValue });

The logic is now neatly encapsulated in one place (AppraisalService.js), ready to be reused and easy to update.


Key Concepts Checklist


Mission Log: Quest - “Hire an Appraiser”

A new tax has been levied on all discovered treasure. We need to hire an Appraiser to calculate the value of a treasure after this new “Crown’s Tax” is applied.


Mission Debrief (Review & Outcomes)

Mission complete, Navigator. Your command structure is now far more robust. You’ve successfully delegated a complex task to a specialist, keeping your controller clean and your core logic organized and reusable.

By creating the AppraisalService, you’ve built an asset that can be called from anywhere. If the Crown’s Tax rate changes from 15% to 20%, you only have to change it in one place: the service file. Every part of your application that uses this service will be instantly updated.

This is the path to building large, professional applications. Your ability to organize code is just as important as your ability to write it.


Rewards