Introduction
In Strapi, you can modify and customize the code differently. One of the most common ways is to use the register
and bootstrap
functions.
You can learn more about them here.
However, this article will focus on the register
function.
This will allow you to register and initialize the different services available in the application.
You can find it in the src/index.ts
file.
1// import type { Core } from '@strapi/strapi';
2
3export default {
4 /**
5 * An asynchronous register function that runs before
6 * your application is initialized.
7 *
8 * This gives you an opportunity to extend code.
9 */
10 register(/* { strapi }: { strapi: Core.Strapi } */) {},
11
12 /**
13 * An asynchronous bootstrap function that runs before
14 * your application gets started.
15 *
16 * This gives you an opportunity to set up your data model,
17 * run jobs, or perform some special logic.
18 */
19 bootstrap(/* { strapi }: { strapi: Core.Strapi } */) {},
20};
The register
function will be called when the application starts up.
The bootstrap
function will be called when the application gets started.
Register function can be used to register routes/contentTypes/controllers/services/cronJobs/(policies/middlewares).
It takes in the strapi object as an argument, which is the main object used to access all the different functionalities of the application, including services, middleware, content types, and more.
This will allow you to modify how the services are registered and initialized, add new services, or remove existing ones.
This can also be done via a Plugin inside the server/src/register.ts
file. We will look at it later in the article.
Where are all the objects located in the strapi object:
Plugin / API
Plugins: The plugins are registered and available under the strapi.plugins
object. Each plugin can be accessed by its name, strapi.plugins.<pluginName>
API: The APIs are registered and available under the strapi.api
object. Each plugin can be accessed by its name, strapi.api.<contentTypeName>
both strapi.api
and strapi.plugins
objects contain child objects that provide additional functionality to the application.
These child objects include routes, controllers, services, policies, and middleware. They handle incoming HTTP requests, handle the logic, contain reusable utility functions, and are executed before or after specific routes to perform tasks such as authentication, validation, or logging.
Middlewares
Middlewares: The middlewares are registered and available under the strapi.middlewares
object. Each middleware can be accessed by its name, strapi.middlewares.<middlewareName>
. Middlewares can also be accessed through strapi.plugins.<pluginName>.middlewares.<middlewareName>
or strapi.api.<contentTypeName>.middlewares.<middlewareName>
if they are defined within that specific plugin or API.
Policies
Policies: The policies are registered and available under the strapi.policies
object. Each policy can be accessed by its name, strapi.policies.<policyName>
. Policies are used to set the rules and permissions for specific routes in your application; they are executed before the route's action and can be used to perform tasks such as authentication or validation. Policies can also be defined in the strapi.api.<contentTypeName>.policies.<policyName>
and strapi.plugins.<pluginName>.policies.<policyName>
if they are defined within a specific plugin or API.
Services
Services: The services are registered and available under the strapi.services
object. Each service can be accessed by its name, strapi.services.<serviceName>
. Services can also be accessed through strapi.plugins.strapi.api.<contentTypeName>.services.<serviceName>
if they are defined within that specific plugin or API.
Content Types
Content Types: The Types are registered and available under the strapi.contentTypes
object. Each Content Type can be accessed by its name, strapi.contentTypes.<contentTypeName>
. Additionally, it can also be accessed through the strapi.api.<contentTypeName>.contentTypes.<contentTypeName>
or strapi.plugins.<pluginName>.contentTypes.<contentTypeName>
if it is defined within a specific plugin or API.
Components
Components: The components are registered and available under the strapi.components
object. Each component can be accessed by its uid, strapi.components.<componentName>
.
Configurations
Configurations: The configurations are registered and available under the strapi.config
object. Each configuration can be accessed by its key, strapi.config.<configKey>
.
Cron Jobs
Cron Jobs: Cron jobs are stored in the config and must be enabled to work. You can navigate to the strapi.config.cron
object to access them. All cron jobs are stored within this object.
You can find more details on how to create and configure cron jobs in the Strapi documentation: link to docs
Injecting a Custom Middleware via the Register Function in Strapi
1. Setup
Set up a standard strapi instance. You can do so by running npx create-strapi-app@latest my-strapi-app
and following the instructions.
Once you have created the app, you can change the directory by running cd my-strapi-app
and then start it by running npm run develop
.
You will be asked to create your first Admin User.
Once you have done that, go ahead and, in the content-type-builder, create a new contentType named special
with the following field title,
which will be a short text field.
Click continue and add your first field.
Now that that is done let's look at how to inject custom middleware into a route.
2. Injecting a Custom Middleware into a Route
To inject a custom middleware into a route, you must first define your middleware and then register it within the desired route.
Here's an example of how you can achieve this:
- Define the Middleware:
Create a file named
custom-middleware.ts
in yoursrc/middlewares
folder:
1export default (config, { strapi }) => {
2 // Custom middleware logic
3 return async (ctx, next) => {
4 console.log("Custom middleware executed");
5 await next();
6 };
7};
- To test if this succeeded, we will start strapi up in console mode, aka
npm run strapi console.
Then, after it is loaded, you are an interactive console version of strapi where you will type instrapi.middlewares,
and you should now see in the list'global::custom-middleware': [Function (anonymous)],
Notice that we can see our newly created middleware in the list.
- Register the Middleware:
In your
register
function in thesrc/index.ts
file, you can inject this middleware into a specific route:
1// src/index.ts
2import type { Core } from "@strapi/strapi";
3
4export default {
5 /**
6 * An asynchronous register function that runs before
7 * your application is initialized.
8 *
9 * This gives you an opportunity to extend code.
10 */
11 register({ strapi }: { strapi: Core.Strapi }) {
12 const middleware = "global::custom-middleware";
13 const routesPath = strapi.apis.special.routes.special.routes;
14
15 console.log(routesPath, "routesPath");
16 console.log(middleware, "middleware");
17
18 const registerIndex = routesPath.findIndex(
19 (route) => route.path === "/specials" && route.method === "GET"
20 );
21
22 console.log(registerIndex, "registerIndex");
23
24 const registerRoute = routesPath[registerIndex];
25 if (registerRoute.config.middlewares === undefined) {
26 registerRoute.config.middlewares = [];
27 }
28 registerRoute.config.middlewares.push(middleware);
29 },
30
31 /**
32 * An asynchronous bootstrap function that runs before
33 * your application gets started.
34 *
35 * This gives you an opportunity to set up your data model,
36 * run jobs, or perform some special logic.
37 */
38 bootstrap(/* { strapi }: { strapi: Core.Strapi } */) {},
39};
- To test if this worked as expected, make a GET request to
http://localhost:1337/api/specials
. Now you should see a console.log in your strapi console.
Please set the permissions in the users-permissions
plugin to public
so that the route is accessible.
You should see the following in your strapi console:
Note: you can extract the code in the src/index.ts
file into a function and then call it in the register
function. We will do this in the next example.
Using Register Function Inside a Plugin
Let's start by creating a simple plugin that we will use to inject middleware into a route.
We will use the Plugin CLI to create a new plugin.
We will run the following command to create a new plugin:
npx @strapi/sdk-plugin@latest init my-plugin
You will be prompted to enter the following information:
✔ plugin name … my-plugin
✔ plugin display name … My Plugin
✔ plugin description … Testing things
✔ plugin author name … Paul Brats
✔ plugin author email … paul.bratslavsky@strapi.io
✔ git url …
✔ plugin license … MIT
✔ register with the admin panel? … yes
✔ register with the server? … yes
✔ use editorconfig? … yes
✔ use eslint? … no
✔ use prettier? … yes
✔ use typescript? … yes
This will create a new plugin in the src/plugins
folder.
To enable your plugin by adding the following in config/plugins.ts.
1export default {
2 // ...
3 "my-plugin": {
4 enabled: true,
5 resolve: "./src/plugins/my-plugin",
6 },
7 // ...
8};
Now that we have our plugin let's see how to inject middleware into a route.
To keep our code clean, we will create a src/plugins/my-plugin/server/src/utils
folder with an index.ts
file containing all the functions we will need.
But first, let's create a new middleware inside the src/plugins/my-plugin/server/src/middlewares
folder called custom-plugin-middleware.ts
. We will recreate the middleware example we created in the previous example.
1export default (config, { strapi }) => {
2 // Custom middleware logic
3 return async (ctx, next) => {
4 console.log("Custom Plugin Middleware Executed");
5 await next();
6 };
7};
Remember to export the middleware from the index.ts
file. This way, if we have multiple middleware, we can export them from the same file.
1import customPluginMiddleware from "./custom-plugin-middleware";
2
3export default {
4 customPluginMiddleware,
5};
Remember, if you need to find the middleware name, you can use the following command:
npm run strapi console
followed by:
strapi.middlewares
If you don't see the middleware, you will need to rebuild the plugin by running the following:
cd src/plugins/my-plugin
npm run build
You can also run the plugin in watch mode by running:
npm run watch
I will do that now, so I don't have to rebuild the plugin every time I change.
Now, let's create the src/plugins/my-plugin/server/src/utils/index.ts
file and add the following function:
1import type { Core } from "@strapi/strapi";
2
3const registerDocumentMiddleware = (strapi: Core.Strapi) => {
4 const middleware = "plugin::my-plugin.customPluginMiddleware";
5 const routesPath = strapi.apis.special.routes.special.routes;
6
7 const registerIndex = routesPath.findIndex(
8 (route) => route.path === "/specials" && route.method === "GET"
9 );
10
11 const registerRoute = routesPath[registerIndex];
12
13 if (registerRoute.config.middlewares === undefined) {
14 registerRoute.config.middlewares = [];
15 }
16 registerRoute.config.middlewares.push(middleware);
17};
18
19export { registerDocumentMiddleware };
Finally, let's import our registerDocumentMiddleware
function in to src/plugins/my-plugin/server/src/register.ts
file.
1import type { Core } from "@strapi/strapi";
2import { registerDocumentMiddleware } from "./utils";
3
4const register = ({ strapi }: { strapi: Core.Strapi }) => {
5 registerDocumentMiddleware(strapi);
6};
7
8export default register;
Since we are now using our plugin to inject our middleware, let's remove the code from the src/index.ts
that we used previously or just comment it out.
The file should now look like this:
1import type { Core } from "@strapi/strapi";
2
3export default {
4 /**
5 * An asynchronous register function that runs before
6 * your application is initialized.
7 *
8 * This gives you an opportunity to extend code.
9 */
10 register({ strapi }: { strapi: Core.Strapi }) {
11 // const middleware = "global::custom-middleware";
12 // const routesPath = strapi.apis.special.routes.special.routes;
13 // console.log(routesPath, "routesPath");
14 // console.log(middleware, "middleware");
15 // const registerIndex = routesPath.findIndex(
16 // (route) => route.path === "/specials" && route.method === "GET"
17 // );
18 // console.log(registerIndex, "registerIndex");
19 // const registerRoute = routesPath[registerIndex];
20 // if (registerRoute.config.middlewares === undefined) {
21 // registerRoute.config.middlewares = [];
22 // }
23 // registerRoute.config.middlewares.push(middleware);
24 },
25
26 /**
27 * An asynchronous bootstrap function that runs before
28 * your application gets started.
29 *
30 * This gives you an opportunity to set up your data model,
31 * run jobs, or perform some special logic.
32 */
33 bootstrap(/* { strapi }: { strapi: Core.Strapi } */) {},
34};
Now that we have commented out the code from the src/index.ts
file, we can start to test if the plugin is working as expected. Don't forget to build the plugin by running npm run build
and restart the strapi instance by running npm run strapi develop.
When we make a GET request to http://localhost:1337/api/specials
we should see our new console.log coming from the plugin middleware.
Congrats, this part is done. Now, to explain why this is useful. This means a plugin can dynamically inject middleware into your strapi instance; this is cool because it means you can have a plugin responsible for adding custom middleware to your strapi instance. You can disable/remove the plugin if you want to remove it.
This is nice because it allows you to have plugins that add custom data or functionality to requests or any custom logic, like validation or caching.
Now, let's see how we can programmatically add a component to a content type.
3. Creating a new component programmatically.
Previously, we created a content type called Special in the setup. Let's learn how to programmatically create a new component in our plugin and inject it into our existing contentType.
Step 1: Create the component.
Let's create a folder called components
in the src/plugins/my-plugin/server/src
folder, create a new file called quote.ts,
and export it.
The code will look like this:
1export const quote = {
2 collectionName: "components_shared_quotes",
3 info: {
4 displayName: "Quote",
5 icon: "indent",
6 },
7 options: {},
8 attributes: {
9 title: {
10 type: "string",
11 },
12 body: {
13 type: "text",
14 },
15 },
16 __filename__: "quote.json",
17 __schema__: {
18 collectionName: "components_custom_quotes",
19 info: {
20 displayName: "Quote",
21 icon: "indent",
22 },
23 options: {},
24 attributes: {
25 title: {
26 type: "string",
27 },
28 body: {
29 type: "text",
30 },
31 },
32 __filename__: "quote.json",
33 },
34 uid: "custom.quote",
35 category: "custom",
36 modelType: "component",
37 modelName: "quote",
38 globalId: "ComponentSharedQuote",
39};
Step 2: Create registerComponent function.
Inside the src/plugins/my-plugin/server/src/utils/index.ts
file, let's create a new function called registerComponent
by adding the following code:
1import { quote } from "../components/quote";
2
3const registerComponent = (strapi: Core.Strapi) => {
4 strapi.components[`custom.quote`] = quote as any;
5
6 const attributes = strapi.contentType("api::special.special").attributes;
7 const schema = strapi.contentType("api::special.special")["__schema__"]
8 .attributes;
9
10 const componentReference = {
11 type: "component",
12 repeatable: false,
13 component: "custom.quote",
14 };
15 // @ts-expect-error - attributes waiting to be updated https://github.com/strapi/strapi/blob/cc6c39db185a337e2eafce8bcf06544351e92cc5/packages/core/types/src/struct/schema.ts#L13
16 attributes["customField"] = componentReference;
17 schema["customField"] = componentReference;
18};
Don't forget to add it to the exports.
1export { registerDocumentMiddleware, registerComponent };
Step 3: Import the function.
Finally, let's import the function in the src/plugins/my-plugin/server/src/register.ts
file. The updated code should look like this:
1import type { Core } from "@strapi/strapi";
2import { registerDocumentMiddleware, registerComponent } from "./utils";
3
4const register = ({ strapi }: { strapi: Core.Strapi }) => {
5 registerDocumentMiddleware(strapi);
6 registerComponent(strapi);
7};
8
9export default register;
Let's rebuild and restart and see if our component works as expected.
If you navigate to the content-type-builder you should see our new component injected into the Special contentType.
Finally, let's take a look at one more example. We will see how we can inject a cron job programmatically via our plugin.
4. Adding a Cron Job Programmatically via Our Plugin.
Step 1: Define the Cron Job.
Let's create a new folder called cron-jobs
in the src/plugins/my-plugin/server/src
folder, create a new file called my-first-job.ts,
and export it.
The code will look like the following:
1export const cronJob = {
2 task: () => {
3 console.log("cron job");
4 },
5 // only run once after 10 seconds
6 options: {
7 rule: "* * * * * *",
8 // start 10 seconds from now
9 start: new Date(Date.now() + 10000),
10 // end 20 seconds from now
11 end: new Date(Date.now() + 20000),
12 },
13};
This example is borrowed from the Strapi Documentation.
We programmatically add and enable the cron job in our configuration.
Step 2: Create registerCronJob function.
Inside the src/plugins/my-plugin/server/src/utils/index.ts
file, let's create a new function called registerCronJob
by adding the following code:
1import { cronJob } from "../cron-jobs/my-first-cron-job";
2
3const registerCronJob = (strapi: Core.Strapi) => {
4 const serverConfig = strapi.config.server;
5
6 if (serverConfig?.cron?.tasks === undefined) {
7 serverConfig.cron.tasks = {};
8 }
9
10 serverConfig.cron.enabled = true;
11 serverConfig.cron.tasks["myJob"] = cronJob;
12};
Finally, let's import the function in the src/plugins/my-plugin/server/src/register.ts
file. The updated code should look like this:
1import type { Core } from "@strapi/strapi";
2import {
3 registerDocumentMiddleware,
4 registerCronJob,
5 registerComponent,
6} from "./utils";
7
8const register = ({ strapi }: { strapi: Core.Strapi }) => {
9 registerDocumentMiddleware(strapi);
10 registerComponent(strapi);
11 registerCronJob(strapi);
12};
13
14export default register;
Let's rebuild and restart and see if our cron job works as expected. You should see the following in your console. Just be aware that it will start running after 10 seconds, run for 10 seconds, and then stop.
Conclusion
We just learned how to use the register function in Strapi and how to use it in plugins to inject custom middleware, components, and cron jobs.
Key Takeaways:
Understanding the register Function: The register function runs before the Strapi application initializes and allows developers to modify, extend, or inject functionality into the application.
Strapi Object Structure: We learned where different elements (APIs, plugins, middlewares, policies, services, content types, components, configurations, and cron jobs) are located within the Strapi object, providing direct access paths.
Injecting Custom Middleware: We learned how to define, register, and apply custom middleware to specific routes using the register function.
- Using the Register Function Inside a Plugin: We learned how to create a Strapi plugin and leverage the register function to inject middleware dynamically.
Programmatically Creating Components: We learned how to register new components and associate them with existing content types within Strapi using the register function.
- Adding Cron Jobs Programmatically: We learned to inject cron jobs into Strapi via plugins, allowing scheduled tasks without manual configuration.
I hope you enjoyed this blog post. If you have any questions, please leave a comment below.
Github Project Repo
You can find the complete code for this project in the following Github repo.
Strapi Open Office Hours
If you have any questions about Strapi 5 or would like to stop by and say hi, you can join us at Strapi's Discord Open Office Hours Monday through Friday at 12:30 pm - 1:30 pm CST: Strapi Discord Open Office Hours.