Services
Page summary:
Services store reusable functions to keep controllers concise and follow DRY principles. This documentation explains generating or extending services with
createCoreServiceand organizing them for APIs or plugins.
Services are a set of reusable functions. They are particularly useful to respect the "don’t repeat yourself" (DRY) programming concept and to simplify controllers logic.

Implementation
Services can be generated or added manually. Strapi provides a createCoreService factory function that automatically generates core services and allows building custom ones or extend or replace the generated services.
Adding a new service
A new service can be implemented:
- with the interactive CLI command
strapi generate - or manually by creating a JavaScript file in the appropriate folder (see project structure):
./src/api/[api-name]/services/for API services- or
./src/plugins/[plugin-name]/services/for plugin services.
To manually create a service, export a factory function that returns the service implementation (i.e. an object with methods). This factory function receives the strapi instance:
- JavaScript
- TypeScript
const { createCoreService } = require('@strapi/strapi').factories;
module.exports = createCoreService('api::restaurant.restaurant', ({ strapi }) => ({
// Method 1: Creating an entirely new custom service
async exampleService(...args) {
let response = { okay: true }
if (response.okay === false) {
return { response, error: true }
}
return response
},
// Method 2: Wrapping a core service (leaves core logic in place)
async find(...args) {
// Calling the default core controller
const { results, pagination } = await super.find(...args);
// some custom logic
results.forEach(result => {
result.counter = 1;
});
return { results, pagination };
},
// Method 3: Replacing a core service
async findOne(documentId, params = {}) {
return strapi.documents('api::restaurant.restaurant').findOne({
documentId,
// Use super to keep core fetch parameter formatting
...super.getFetchParams(params),
});
}
}));
import { factories } from '@strapi/strapi';
export default factories.createCoreService('api::restaurant.restaurant', ({ strapi }) => ({
// Method 1: Creating an entirely custom service
async exampleService(...args) {
let response = { okay: true }
if (response.okay === false) {
return { response, error: true }
}
return response
},
// Method 2: Wrapping a core service (leaves core logic in place)
async find(...args) {
// Calling the default core controller
const { results, pagination } = await super.find(...args);
// some custom logic
results.forEach(result => {
result.counter = 1;
});
return { results, pagination };
},
// Method 3: Replacing a core service
async findOne(documentId, params = {}) {
return strapi.documents('api::restaurant.restaurant').findOne({
documentId,
// Use super to keep core fetch parameter formatting
...super.getFetchParams(params) }) as any;
}
}));
To get started creating your own services, see Strapi's built-in functions in the Document Service API documentation.
Example of a custom email service (using Nodemailer)
The goal of a service is to store reusable functions. A sendNewsletter service could be useful to send emails from different functions in our codebase that have a specific purpose:
- JavaScript
- TypeScript
const { createCoreService } = require('@strapi/strapi').factories;
const nodemailer = require('nodemailer'); // Requires nodemailer to be installed (npm install nodemailer)
// Create reusable transporter object using SMTP transport.
const transporter = nodemailer.createTransport({
service: 'Gmail',
auth: {
user: 'user@gmail.com',
pass: 'password',
},
});
module.exports = createCoreService('api::restaurant.restaurant', ({ strapi }) => ({
sendNewsletter(from, to, subject, text) {
// Setup e-mail data.
const options = {
from,
to,
subject,
text,
};
// Return a promise of the function that sends the email.
return transporter.sendMail(options);
},
}));
import { factories } from '@strapi/strapi';
const nodemailer = require('nodemailer'); // Requires nodemailer to be installed (npm install nodemailer)
// Create reusable transporter object using SMTP transport.
const transporter = nodemailer.createTransport({
service: 'Gmail',
auth: {
user: 'user@gmail.com',
pass: 'password',
},
});
export default factories.createCoreService('api::restaurant.restaurant', ({ strapi }) => ({
sendNewsletter(from, to, subject, text) {
// Setup e-mail data.
const options = {
from,
to,
subject,
text,
};
// Return a promise of the function that sends the email.
return transporter.sendMail(options);
},
}));
The service is now available through the strapi.service('api::restaurant.restaurant').sendNewsletter(...args) global variable. It can be used in another part of the codebase, like in the following controller:
- JavaScript
- TypeScript
module.exports = createCoreController('api::restaurant.restaurant', ({ strapi }) => ({
// GET /hello
async signup(ctx) {
const { userData } = ctx.body;
// Store the new user in database.
const user = await strapi.service('plugin::users-permissions.user').add(userData);
// Send an email to validate his subscriptions.
strapi.service('api::restaurant.restaurant').sendNewsletter('welcome@mysite.com', user.email, 'Welcome', '...');
// Send response to the server.
ctx.send({
ok: true,
});
},
}));
export default factories.createCoreController('api::restaurant.restaurant', ({ strapi }) => ({
// GET /hello
async signup(ctx) {
const { userData } = ctx.body;
// Store the new user in database.
const user = await strapi.service('plugin::users-permissions.user').add(userData);
// Send an email to validate his subscriptions.
strapi.service('api::restaurant.restaurant').sendNewsletter('welcome@mysite.com', user.email, 'Welcome', '...');
// Send response to the server.
ctx.send({
ok: true,
});
},
}));
When a new content-type is created, Strapi builds a generic service with placeholder code, ready to be customized.