docs: migrate guides to TSDoc references (#6100)
This commit is contained in:
@@ -115,7 +115,7 @@ For example, if your API route accepts an author ID and a post ID, the path to y
|
||||
|
||||
## CORS Configuration
|
||||
|
||||
The `cors` middleware, which enables Cross-Origin Resource Sharing (CORS), is automatically applied on custom API Routes defined under the `/store` or `/admin` path prefixes based on the respective [store_cors and admin_cors configurations](../backend/configurations.md#admin_cors-and-store_cors).
|
||||
The `cors` middleware, which enables Cross-Origin Resource Sharing (CORS), is automatically applied on custom API Routes defined under the `/store` or `/admin` path prefixes based on the respective [store_cors and admin_cors configurations](../../references/medusa_config/interfaces/medusa_config.ConfigModule.mdx#admin_cors).
|
||||
|
||||
To add CORS configurations to custom API routes under other path prefixes, or override the CORS configurations added by default, define a [middleware](./add-middleware.mdx) on your API routes and pass it the `cors` middleware. For example:
|
||||
|
||||
|
||||
@@ -1,476 +0,0 @@
|
||||
---
|
||||
description: 'Learn about the different configurations available in a Medusa backend. This includes configurations related to the database, CORS, plugins, and more.'
|
||||
---
|
||||
|
||||
# Configure Medusa Backend
|
||||
|
||||
This document provides a reference of all accepted Medusa configurations in `medusa-config.js`.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
This document assumes you already followed along with the [Prepare Environment documentation](./prepare-environment.mdx) and have [installed a Medusa backend](./install.mdx#create-a-medusa-backend).
|
||||
|
||||
---
|
||||
|
||||
## Medusa Configurations File
|
||||
|
||||
The configurations for your Medusa backend are in `medusa-config.js` located in the root of your Medusa project. The configurations include database, modules, and plugin configurations, among other configurations.
|
||||
|
||||
`medusa-config.js` exports an object having the following properties:
|
||||
|
||||
| Property Name | Description | Required |
|
||||
| --- | --- | --- |
|
||||
| [projectConfig](#projectconfig) | An object that holds general configurations related to the Medusa backend, such as database or CORS configurations. | Yes |
|
||||
| [plugins](#plugins) | An array of plugin configurations that defines what plugins are installed and optionally specifies each of their configurations. | No |
|
||||
| [modules](#modules) | An object that defines what modules are installed and optionally specifies each of their configurations. | No |
|
||||
| [featureFlags](#featureflags) | An object that enables or disables features guarded by a feature flag. | No |
|
||||
|
||||
For example:
|
||||
|
||||
```js title="medusa-config.js"
|
||||
module.exports = {
|
||||
projectConfig,
|
||||
plugins,
|
||||
modules,
|
||||
featureFlags,
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Environment Variables
|
||||
|
||||
Many of the configurations mentioned in this guide are recommended to have their values set in environment variables and referenced within `medusa-config.js`.
|
||||
|
||||
During development, you can set your environment variables in the `.env` file at the root of your Medusa backend project. In production, setting the environment variables depends on the hosting provider.
|
||||
|
||||
---
|
||||
|
||||
## projectConfig
|
||||
|
||||
This section includes all configurations that belong to the `projectConfig` property in the configuration object exported by `medusa-config.js`. The `projectConfig` property is an object, and each of the configurations in this section is added to this object as key-value pairs.
|
||||
|
||||
### admin_cors and store_cors
|
||||
|
||||
The Medusa backend’s API Routes are protected by Cross-Origin Resource Sharing (CORS). So, only allowed URLs or URLs matching a specified pattern can send requests to the backend’s API Routes.
|
||||
|
||||
`admin_cors` is used to specify the accepted URLs or patterns for admin API Routes, and `store_cors` is used to specify the accepted URLs or patterns for store API Routes.
|
||||
|
||||
For both the `admin_cors` and `store_cors`, the value is expected to be a string. This string can be a comma-separated list of accepted origins. Every origin in that list can be of the following types:
|
||||
|
||||
1. A URL. For example, `http://localhost:8000`. The URL shouldn’t end with a backslash.
|
||||
2. A regular expression pattern that can match more than one origin. For example, `.example.com`. The regex pattern that the backend tests for is `^([\/~@;%#'])(.*?)\1([gimsuy]*)$`.
|
||||
|
||||
Here are some examples of common use cases:
|
||||
|
||||
```bash
|
||||
# Allow different ports locally starting with 700
|
||||
ADMIN_CORS=/http:\/\/localhost:700\d+$/
|
||||
|
||||
# Allow any origin ending with vercel.app. For example, storefront.vercel.app
|
||||
STORE_CORS=/vercel\.app$/
|
||||
|
||||
# Allow all HTTP requests
|
||||
ADMIN_CORS=/http:\/\/*/
|
||||
```
|
||||
|
||||
Typically, the value of these configurations would be set in an environment variable and referenced in `medusa-config.js`:
|
||||
|
||||
```js title="medusa-config.js"
|
||||
module.exports = {
|
||||
projectConfig: {
|
||||
admin_cors: process.env.ADMIN_CORS,
|
||||
store_cors: process.env.STORE_CORS,
|
||||
// ...
|
||||
},
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
If you’re adding the value directly within `medusa-config.js`, make sure to add an extra escaping `/` for every backslash in the pattern. For example:
|
||||
|
||||
```js title="medusa-config.js"
|
||||
module.exports = {
|
||||
projectConfig: {
|
||||
admin_cors: "/http:\\/\\/localhost:700\\d+$/",
|
||||
store_cors: "/vercel\\.app$/",
|
||||
// ...
|
||||
},
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### cookie_secret
|
||||
|
||||
A string that is used to create cookie tokens. Although this configuration option is not required, it’s highly recommended to set it for better security. It’s also recommended to generate a random string.
|
||||
|
||||
In a development environment, if this option is not set the default secret is `supersecret` However, in production, if this configuration is not set an error will be thrown and your backend will crash.
|
||||
|
||||
Typically, the value of this configuration would be set in an environment variable and referenced in `medusa-config.js`.
|
||||
|
||||
```js title="medusa-config.js"
|
||||
module.exports = {
|
||||
projectConfig: {
|
||||
cookie_secret: process.env.COOKIE_SECRET,
|
||||
// ...
|
||||
},
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### http_compression
|
||||
|
||||
This configuration enables HTTP compression from the application layer. If you have access to the HTTP server, the recommended approach would be to enable it there. However, some platforms don't offer access to the HTTP layer and in those cases, this is a good alternative.
|
||||
|
||||
Its value is an object that has the following properties:
|
||||
|
||||
- `enabled`: A boolean flag that indicates whether HTTP compression is enabled. It is disabled by default.
|
||||
- `level`: A `number` value that indicates the level of zlib compression to apply to responses. A higher level will result in better compression but will take longer to complete. A lower level will result in less compression but will be much faster. The default value is 6.
|
||||
- `memLevel`: A `number` value that specifies how much memory should be allocated to the internal compression state. It's an integer in the range of 1 (minimum level) and 9 (maximum level). The default value is `8`.
|
||||
- `threshold`: A `number` or a `string` value in bytes that specifies the minimum response body size that compression is applied on. This is the number of bytes or any string accepted by the bytes module. The default value is `1024`.
|
||||
|
||||
If you enable HTTP compression and you want to disable it for specific API Routes, you can pass in the request header `"x-no-compression": true`.
|
||||
|
||||
```js title="medusa-config.js"
|
||||
module.exports = {
|
||||
projectConfig: {
|
||||
http_compression: {
|
||||
enabled: true,
|
||||
level: 6,
|
||||
memLevel: 8,
|
||||
threshold: 1024,
|
||||
},
|
||||
// ...
|
||||
},
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### jwt_secret
|
||||
|
||||
A string that is used to create authentication tokens. Although this configuration option is not required, it’s highly recommended to set it for better security. It’s also recommended to generate a random string.
|
||||
|
||||
In a development environment, if this option is not set the default secret is `supersecret` However, in production, if this configuration is not set an error will be thrown and your backend will crash.
|
||||
|
||||
Typically, the value of this configuration would be set in an environment variable and referenced in `medusa-config.js`.
|
||||
|
||||
```js title="medusa-config.js"
|
||||
module.exports = {
|
||||
projectConfig: {
|
||||
jwt_secret: process.env.JWT_SECRET,
|
||||
// ...
|
||||
},
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### database_database
|
||||
|
||||
The name of the database to connect to. If provided in `database_url`, then it’s not necessary to include it.
|
||||
|
||||
Make sure to create the PostgreSQL database before using it. You can check how to create a database in [PostgreSQL's documentation](https://www.postgresql.org/docs/current/sql-createdatabase.html).
|
||||
|
||||
```js title="medusa-config.js"
|
||||
module.exports = {
|
||||
projectConfig: {
|
||||
database_database: "medusa-store",
|
||||
// ...
|
||||
},
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### database_extra
|
||||
|
||||
An object that includes additional configurations to pass to the database connection. You can pass any configuration. One defined configuration to pass is `ssl` which enables support for TLS/SSL connections.
|
||||
|
||||
This is useful for production databases, which can be supported by setting the `rejectUnauthorized` attribute of `ssl` object to `false`. During development, it’s recommended not to pass this option.
|
||||
|
||||
```js title="medusa-config.js"
|
||||
module.exports = {
|
||||
projectConfig: {
|
||||
database_extra:
|
||||
process.env.NODE_ENV !== "development"
|
||||
? { ssl: { rejectUnauthorized: false } }
|
||||
: {},
|
||||
// ...
|
||||
},
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### database_logging
|
||||
|
||||
This configuration specifies what messages to log. Its value can be one of the following:
|
||||
|
||||
- (default) A boolean value that indicates whether any messages should be logged or not. By default, if no value is provided, the value will be `false`.
|
||||
- The string value `all` that indicates all types of messages should be logged.
|
||||
- An array of log-level strings to indicate which type of messages to show in the logs. The strings can be `query`, `schema`, `error`, `warn`, `info`, `log`, or `migration`. Refer to [Typeorm’s documentation](https://typeorm.io/logging#logging-options) for more details on what each of these values means.
|
||||
|
||||
```js title="medusa-config.js"
|
||||
module.exports = {
|
||||
projectConfig: {
|
||||
database_logging: [
|
||||
"query", "error",
|
||||
],
|
||||
// ...
|
||||
},
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### database_schema
|
||||
|
||||
A string indicating the database schema to connect to. This is not necessary to provide if you’re using the default schema, which is `public`.
|
||||
|
||||
```js title="medusa-config.js"
|
||||
module.exports = {
|
||||
projectConfig: {
|
||||
database_schema: "custom",
|
||||
// ...
|
||||
},
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### database_type
|
||||
|
||||
A string indicating the type of database to connect to. At the moment, only `postgres` is accepted, which is also the default value.
|
||||
|
||||
```js title="medusa-config.js"
|
||||
module.exports = {
|
||||
projectConfig: {
|
||||
database_type: "postgres",
|
||||
// ...
|
||||
},
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### database_url
|
||||
|
||||
A string indicating the connection URL of the database. Typically, the connection URL would be set in an environment variable, and the variable would be referenced in `medusa-config.js`.
|
||||
|
||||
The format of the connection URL for PostgreSQL is:
|
||||
|
||||
```bash
|
||||
postgres://[user][:password]@[host][:port]/[dbname]
|
||||
```
|
||||
|
||||
Where:
|
||||
|
||||
- `[user]`: (required) your PostgreSQL username. If not specified, the system's username is used by default. The database user that you use must have create privileges. If you're using the `postgres` superuser, then it should have these privileges by default. Otherwise, make sure to grant your user create privileges. You can learn how to do that in [PostgreSQL's documentation](https://www.postgresql.org/docs/current/ddl-priv.html).
|
||||
- `[:password]`: an optional password for the user. When provided, make sure to put `:` before the password.
|
||||
- `[host]`: (required) your PostgreSQL host. When run locally, it should be `localhost`.
|
||||
- `[:post]`: an optional port that the PostgreSQL server is listening on. By default, it's `5432`. When provided, make sure to put `:` before the port.
|
||||
- `[dbname]`: (required) the name of the database.
|
||||
|
||||
For example, you can set the following database URL in your environment variables:
|
||||
|
||||
```bash
|
||||
DATABASE_URL=postgres://postgres@localhost/medusa-store
|
||||
```
|
||||
|
||||
You can learn more about the connection URL format in [PostgreSQL’s documentation](https://www.postgresql.org/docs/current/libpq-connect.html).
|
||||
|
||||
```js title="medusa-config.js"
|
||||
module.exports = {
|
||||
projectConfig: {
|
||||
database_url: process.env.DATABASE_URL,
|
||||
// ...
|
||||
},
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### redis_url
|
||||
|
||||
This configuration is used to specify the URL to connect to Redis. This is only used for scheduled jobs. If you omit this configuration, scheduled jobs will not work.
|
||||
|
||||
:::note
|
||||
|
||||
You must first have Redis installed. You can refer to [Redis's installation guide](https://redis.io/docs/getting-started/installation/).
|
||||
|
||||
:::
|
||||
|
||||
The Redis connection URL has the following format:
|
||||
|
||||
```bash
|
||||
redis[s]://[[username][:password]@][host][:port][/db-number]
|
||||
```
|
||||
|
||||
For a local Redis installation, the connection URL should be `redis://localhost:6379` unless you’ve made any changes to the Redis configuration during installation.
|
||||
|
||||
Typically, the value would be added as an environment variable and referenced in `medusa-config.js`.
|
||||
|
||||
```js title="medusa-config.js"
|
||||
module.exports = {
|
||||
projectConfig: {
|
||||
redis_url: process.env.REDIS_URL,
|
||||
// ...
|
||||
},
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### redis_prefix
|
||||
|
||||
The prefix set on all keys stored in Redis. The default value is `sess:`. If this configuration option is provided, it is prepended to `sess:`.
|
||||
|
||||
```js title="medusa-config.js"
|
||||
module.exports = {
|
||||
projectConfig: {
|
||||
redis_prefix: "medusa:",
|
||||
// ...
|
||||
},
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### redis_options
|
||||
|
||||
An object of options to pass ioredis. You can refer to [ioredis’s RedisOptions documentation](https://redis.github.io/ioredis/index.html#RedisOptions) for the list of available options.
|
||||
|
||||
```js title="medusa-config.js"
|
||||
module.exports = {
|
||||
projectConfig: {
|
||||
redis_options: {
|
||||
connectionName: "medusa",
|
||||
},
|
||||
// ...
|
||||
},
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### session_options
|
||||
|
||||
An object of options to pass to `express-session`. The object can have the following properties:
|
||||
|
||||
- `name`: A string indicating the name of the session ID cookie to set in the response (and read from in the request). The default value is `connect.sid`. Refer to [express-session’s documentation](https://www.npmjs.com/package/express-session#name) for more details.
|
||||
- `resave`: A boolean value that indicates whether the session should be saved back to the session store, even if the session was never modified during the request. The default value is `true`. Refer to [express-session’s documentation](https://www.npmjs.com/package/express-session#resave) for more details.
|
||||
- `rolling`: A boolean value that indicates whether the session identifier cookie should be force-set on every response. The default value is `false`. Refer to [express-session’s documentation](https://www.npmjs.com/package/express-session#rolling) for more details.
|
||||
- `saveUninitialized`: A boolean value that indicates whether a session that is "uninitialized" is forced to be saved to the store. The default value is `true`. Refer to [express-session’s documentation](https://www.npmjs.com/package/express-session#saveUninitialized) for more details.
|
||||
- `secret`: A string that indicates the secret to sign the session ID cookie. By default, the value of [cookie_secret](#cookie_secret) will be used. Refer to [express-session’s documentation](https://www.npmjs.com/package/express-session#secret) for details.
|
||||
- `ttl`: A number is used when calculating the `Expires` `Set-Cookie` attribute of cookies. By default, it’ll be `10 * 60 * 60 * 1000`. Refer to [express-session’s documentation](https://www.npmjs.com/package/express-session#cookiemaxage) for details.
|
||||
|
||||
```js title="medusa-config.js"
|
||||
module.exports = {
|
||||
projectConfig: {
|
||||
session_options: {
|
||||
name: "custom",
|
||||
},
|
||||
// ...
|
||||
},
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## plugins
|
||||
|
||||
On your Medusa backend, you can use Plugins to add custom features or integrate third-party services. For example, installing a plugin to use Stripe as a payment processor.
|
||||
|
||||
Aside from installing the plugin with NPM, you need to pass the plugin you installed into the `plugins` array defined in `medusa-config.js`.
|
||||
|
||||
The items in the array can either be:
|
||||
|
||||
- A string, which is the name of the plugin to add. You can pass a plugin as a string if it doesn’t require any configurations.
|
||||
- An object having the following properties:
|
||||
- `resolve`: A string indicating the name of the plugin.
|
||||
- `options`: An object that includes the plugin’s options. These options vary for each plugin, and you should refer to the plugin’s documentation for details on them.
|
||||
|
||||
For example:
|
||||
|
||||
```js title="medusa-config.js"
|
||||
module.exports = {
|
||||
plugins: [
|
||||
`medusa-my-plugin-1`,
|
||||
{
|
||||
resolve: `medusa-my-plugin`,
|
||||
options: {
|
||||
apiKey: `test`, // or use env variables
|
||||
},
|
||||
},
|
||||
// ...
|
||||
],
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
You can refer to the [Plugins Overview documentation](../../plugins/overview.mdx) for a list of available official plugins.
|
||||
|
||||
---
|
||||
|
||||
## modules
|
||||
|
||||
In Medusa, commerce and core logic are modularized to allow developers to extend or replace certain modules with custom implementations.
|
||||
|
||||
Aside from installing the module with NPM, you need to add it to the exported object in `medusa-config.js`.
|
||||
|
||||
The keys of the `modules` configuration object refer to the type of module. Its value can be one of the following:
|
||||
|
||||
1. A boolean value that indicates whether the module type is enabled.
|
||||
2. A string value that indicates the name of the module to be used for the module type. This can be used if the module does not require any options.
|
||||
3. An object having the following properties, but typically you would mainly use the `resolve` and `options` properties only:
|
||||
1. `resolve`: a string indicating the name of the module.
|
||||
2. `options`: an object indicating the options to pass to the module. These options vary for each module, and you should refer to the module’s documentation for details on them.
|
||||
3. `resources`: a string indicating whether the module shares the dependency container with the Medusa core. Its value can either be `shared` or `isolated`. Refer to the [Modules documentation](../modules/create.mdx#module-scope) for more details.
|
||||
4. `alias`: a string indicating a unique alias to register the module under. Other modules can’t use the same alias.
|
||||
5. `main`: a boolean value indicating whether this module is the main registered module. This is useful when an alias is used.
|
||||
|
||||
For example:
|
||||
|
||||
```js title="medusa-config.js"
|
||||
module.exports = {
|
||||
modules: {
|
||||
eventBus: {
|
||||
resolve: "@medusajs/event-bus-local",
|
||||
},
|
||||
cacheService: {
|
||||
resolve: "@medusajs/cache-redis",
|
||||
options: {
|
||||
redisUrl: process.env.CACHE_REDIS_URL,
|
||||
ttl: 30,
|
||||
},
|
||||
},
|
||||
// ...
|
||||
},
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
Learn more about [Modules and how to create and use them](../modules/overview.mdx).
|
||||
|
||||
---
|
||||
|
||||
## featureFlags
|
||||
|
||||
Some features in the Medusa backend are guarded by a feature flag. This ensures constant shipping of new features while maintaining the engine’s stability.
|
||||
|
||||
You can specify whether a feature should or shouldn’t be used in your backend by enabling its feature flag. Feature flags can be enabled through environment variables or through this configuration exported in `medusa-config.js`. If you want to use the environment variables method, learn more about it in the [Feature Flags documentation](../feature-flags/toggle.md#method-one-using-environment-variables).
|
||||
|
||||
The `featureFlags` configuration is an object. Its properties are the names of the different feature flags. Each property’s value is a boolean indicating whether the feature flag is enabled.
|
||||
|
||||
You can find available feature flags and their key name [here](https://github.com/medusajs/medusa/tree/master/packages/medusa/src/loaders/feature-flags).
|
||||
|
||||
For example:
|
||||
|
||||
```js title="medusa-config.js"
|
||||
module.exports = {
|
||||
featureFlags: {
|
||||
product_categories: true,
|
||||
// ...
|
||||
},
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
Learn more about [Feature flags and how to toggle them](../feature-flags/overview.mdx).
|
||||
|
||||
:::note
|
||||
|
||||
After enabling a feature flag, make sure to [run migrations](../entities/migrations/overview.mdx#migrate-command) as it may require making changes to the database.
|
||||
|
||||
:::
|
||||
@@ -40,7 +40,7 @@ Defines an entry file, which is useful when starting the Medusa backend with a p
|
||||
|
||||
Defines the Medusa backend’s configurations, including the database configurations, plugins used, modules used, and more.
|
||||
|
||||
**Read more:** [Medusa backend configurations](./configurations.md).
|
||||
**Read more:** [Medusa backend configurations](../../references/medusa_config/interfaces/medusa_config.ConfigModule.mdx).
|
||||
|
||||
### package.json
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ If you run into any errors while installing the CLI tool, check out the [trouble
|
||||
|
||||
:::warning
|
||||
|
||||
If you choose "Skip database setup" you will need to [set the database configurations](./configurations.md#database-configuration) and [run migrations](../entities/migrations/overview.mdx#migrate-command) later.
|
||||
If you choose "Skip database setup" you will need to [set the database configurations](../../references/medusa_config/interfaces/medusa_config.ConfigModule.mdx) and [run migrations](../entities/migrations/overview.mdx#migrate-command) later.
|
||||
|
||||
:::
|
||||
|
||||
|
||||
@@ -66,7 +66,7 @@ The function accepts a parameter of type `SubscriberArgs`, which has the followi
|
||||
- `data`: The data payload of the emitted event. Its type is different for each event. So, make sure to check the [events reference](./events-list.md) for the expected payload of the events your subscriber listens to. You can then pass the expected payload type as a type parameter to `SubscriberArgs`, for example, `Record<string, string>`.
|
||||
- `eventName`: A string indicating the name of the event. This is useful if your subscriber listens to more than one event and you want to differentiate between them.
|
||||
- `container`: The [dependency container](../fundamentals/dependency-injection.md) that allows you to resolve Medusa resources, such as services.
|
||||
- `pluginOptions`: When the subscriber is created within a plugin, this object holds the plugin's options defined in the [Medusa configurations](../backend/configurations.md).
|
||||
- `pluginOptions`: When the subscriber is created within a plugin, this object holds the plugin's options defined in the [Medusa configurations](../../references/medusa_config/interfaces/medusa_config.ConfigModule.mdx).
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -1,474 +0,0 @@
|
||||
---
|
||||
description: "Learn how to create a file service in a Medusa backend or a plugin."
|
||||
---
|
||||
|
||||
# How to Create a File Service
|
||||
|
||||
In this document, you’ll learn how to create a file service in Medusa.
|
||||
|
||||
## Overview
|
||||
|
||||
In this guide, you’ll learn about the steps to implement a file service and the methods you’re required to implement in a file service. You can implement the file service within the Medusa backend codebase or in a plugin.
|
||||
|
||||
The file service you’ll be creating in this guide will be a local file service that allows you to upload files into your Medusa backend’s codebase. This is to give you a realistic example of the implementation of a file service. You’re free to implement the file service as required for your case.
|
||||
|
||||
---
|
||||
|
||||
## Prerequisites
|
||||
|
||||
### (Optional) Multer Types Package
|
||||
|
||||
If you’re using TypeScript, as instructed by this guide, you should install the Multer types package to resolve errors within your file service types.
|
||||
|
||||
To do that, run the following command in the directory of your Medusa backend or plugin:
|
||||
|
||||
```bash npm2yarn
|
||||
npm install @types/multer
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 1: Create the File Service Class
|
||||
|
||||
A file service class is defined in a TypeScript or JavaScript file that’s created in the `src/services` directory. The class must extend the `AbstractFileService` class imported from the `@medusajs/medusa` package.
|
||||
|
||||
Based on services’ naming conventions, the file’s name should be the slug version of the file service’s name without `service`, and the class’s name should be the pascal case of the file service’s name following by `Service`.
|
||||
|
||||
For example, if you’re creating a local file service, the file name would be `local-file.ts`, whereas the class name would be `LocalFileService`.
|
||||
|
||||
:::tip
|
||||
|
||||
You can learn more about services and their naming convention in [this documentation](../services/overview.mdx).
|
||||
|
||||
:::
|
||||
|
||||
For example, create the file `src/services/local-file.ts` with the following content:
|
||||
|
||||
```ts title="src/services/local-file.ts"
|
||||
import { AbstractFileService } from "@medusajs/medusa"
|
||||
import {
|
||||
DeleteFileType,
|
||||
FileServiceGetUploadStreamResult,
|
||||
FileServiceUploadResult,
|
||||
GetUploadedFileType,
|
||||
UploadStreamDescriptorType,
|
||||
} from "@medusajs/types"
|
||||
|
||||
class LocalFileService extends AbstractFileService {
|
||||
async upload(
|
||||
fileData: Express.Multer.File
|
||||
): Promise<FileServiceUploadResult> {
|
||||
throw new Error("Method not implemented.")
|
||||
}
|
||||
async uploadProtected(
|
||||
fileData: Express.Multer.File
|
||||
): Promise<FileServiceUploadResult> {
|
||||
throw new Error("Method not implemented.")
|
||||
}
|
||||
async delete(fileData: DeleteFileType): Promise<void> {
|
||||
throw new Error("Method not implemented.")
|
||||
}
|
||||
async getUploadStreamDescriptor(
|
||||
fileData: UploadStreamDescriptorType
|
||||
): Promise<FileServiceGetUploadStreamResult> {
|
||||
throw new Error("Method not implemented.")
|
||||
}
|
||||
async getDownloadStream(
|
||||
fileData: GetUploadedFileType
|
||||
): Promise<NodeJS.ReadableStream> {
|
||||
throw new Error("Method not implemented.")
|
||||
}
|
||||
async getPresignedDownloadUrl(
|
||||
fileData: GetUploadedFileType
|
||||
): Promise<string> {
|
||||
throw new Error("Method not implemented.")
|
||||
}
|
||||
}
|
||||
|
||||
export default LocalFileService
|
||||
```
|
||||
|
||||
This creates the service `LocalFileService` which, at the moment, adds a general implementation of the methods defined in the abstract class `AbstractFileService`. Notice that the methods' signatures require importing types from the `@medusajs/types` package.
|
||||
|
||||
:::tip
|
||||
|
||||
If you get errors regarding the types not being available in the `@medusajs/types` package, make sure that the latest version of the package is installed:
|
||||
|
||||
```bash npm2yarn
|
||||
npm install @medusajs/types@latest
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
### Using a Constructor
|
||||
|
||||
You can use a constructor to access services and resources registered in the dependency container, to define any necessary clients if you’re integrating a third-party storage service, and to access plugin options if your file service is defined in a plugin.
|
||||
|
||||
For example, the local service’s constructor could be useful to prepare the local upload directory:
|
||||
|
||||
```ts title="src/services/local-file.ts"
|
||||
// ...
|
||||
import * as fs from "fs"
|
||||
|
||||
class LocalFileService extends AbstractFileService {
|
||||
// can also be replaced by an environment variable
|
||||
// or a plugin option
|
||||
protected serverUrl = "http://localhost:9000"
|
||||
protected publicPath = "uploads"
|
||||
protected protectedPath = "protected-uploads"
|
||||
|
||||
constructor(container) {
|
||||
super(container)
|
||||
|
||||
// for public uploads
|
||||
if (!fs.existsSync(this.publicPath)) {
|
||||
fs.mkdirSync(this.publicPath)
|
||||
}
|
||||
|
||||
// for protected uploads
|
||||
if (!fs.existsSync(this.protectedPath)) {
|
||||
fs.mkdirSync(this.protectedPath)
|
||||
}
|
||||
}
|
||||
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
Another example showcasing how to access resources using dependency injection:
|
||||
|
||||
<!-- eslint-disable prefer-rest-params -->
|
||||
|
||||
```ts title="src/services/local-file.ts"
|
||||
type InjectedDependencies = {
|
||||
logger: Logger
|
||||
}
|
||||
|
||||
class LocalFileService extends AbstractFileService {
|
||||
// ...
|
||||
protected logger_: Logger
|
||||
|
||||
constructor({ logger }: InjectedDependencies) {
|
||||
super(...arguments)
|
||||
this.logger_ = logger
|
||||
// ...
|
||||
}
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
You can access the plugin options in the second parameter passed to the constructor:
|
||||
|
||||
```ts title="src/services/local-file.ts"
|
||||
class LocalFileService extends AbstractFileService {
|
||||
protected serverUrl = "http://localhost:9000"
|
||||
// ...
|
||||
|
||||
constructor(
|
||||
container,
|
||||
pluginOptions
|
||||
) {
|
||||
super(container)
|
||||
|
||||
if (pluginOptions?.serverUrl) {
|
||||
this.serverUrl = pluginOptions?.serverUrl
|
||||
}
|
||||
// ...
|
||||
}
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 2: Implement Required Methods
|
||||
|
||||
In this section, you’ll learn about the required methods to implement in the file service.
|
||||
|
||||
### upload
|
||||
|
||||
This method is used to upload a file to the Medusa backend. You must handle the upload logic within this method.
|
||||
|
||||
This method accepts one parameter, which is a [multer file object](http://expressjs.com/en/resources/middleware/multer.html#file-information). The file is uploaded to a temporary directory by default. Among the file’s details, you can access the file’s path in the `path` property of the file object.
|
||||
|
||||
So, for example, you can create a read stream to the file’s content if necessary using the `fs` library:
|
||||
|
||||
```ts
|
||||
fs.createReadStream(file.path)
|
||||
```
|
||||
|
||||
Where `file` is the parameter passed to the `upload` method.
|
||||
|
||||
The method is expected to return an object that has the following properties:
|
||||
|
||||
- `url`: a string indicating the full accessible URL to the file.
|
||||
- `key`: a string indicating the key used to reference the uploaded file. This is useful when retrieving the file later with other methods like the [getDownloadStream](#getdownloadstream).
|
||||
|
||||
An example implementation of this method for the local file service:
|
||||
|
||||
```ts title="src/services/local-file.ts"
|
||||
class LocalFileService extends AbstractFileService {
|
||||
|
||||
async upload(
|
||||
fileData: Express.Multer.File
|
||||
): Promise<FileServiceUploadResult> {
|
||||
const filePath =
|
||||
`${this.publicPath}/${fileData.originalname}`
|
||||
fs.copyFileSync(fileData.path, filePath)
|
||||
return {
|
||||
url: `${this.serverUrl}/${filePath}`,
|
||||
key: filePath,
|
||||
}
|
||||
}
|
||||
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
:::tip
|
||||
|
||||
This example does not account for duplicate names to maintain simplicity in this guide. So, an uploaded file can replace another existing file that has the same name.
|
||||
|
||||
:::
|
||||
|
||||
### uploadProtected
|
||||
|
||||
This method is used to upload a file to the Medusa backend, but to a protected storage. Typically, this would be used to store files that shouldn’t be accessible by using the file’s URL or should only be accessible by authenticated users.
|
||||
|
||||
You must handle the upload logic and the file permissions or private storage configuration within this method.
|
||||
|
||||
This method accepts one parameter, which is a [multer file object](http://expressjs.com/en/resources/middleware/multer.html#file-information). The file is uploaded to a temporary directory by default. Among the file’s details, you can access the file’s path in the `path` property of the file object.
|
||||
|
||||
So, for example, you can create a read stream to the file’s content if necessary using the `fs` library:
|
||||
|
||||
```ts
|
||||
fs.createReadStream(file.path)
|
||||
```
|
||||
|
||||
Where `file` is the parameter passed to the `uploadProtected` method.
|
||||
|
||||
The method is expected to return an object that has the following properties:
|
||||
|
||||
- `url`: a string indicating the full accessible URL to the file.
|
||||
- `key`: a string indicating the key used to reference the uploaded file. This is useful when retrieving the file later with other methods like the [getDownloadStream](#getdownloadstream).
|
||||
|
||||
An example implementation of this method for the local file service:
|
||||
|
||||
```ts title="src/services/local-file.ts"
|
||||
class LocalFileService extends AbstractFileService {
|
||||
|
||||
async uploadProtected(
|
||||
fileData: Express.Multer.File
|
||||
): Promise<FileServiceUploadResult> {
|
||||
const filePath =
|
||||
`${this.protectedPath}/${fileData.originalname}`
|
||||
fs.copyFileSync(fileData.path, filePath)
|
||||
return {
|
||||
url: `${this.serverUrl}/${filePath}`,
|
||||
}
|
||||
}
|
||||
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### delete
|
||||
|
||||
This method is used to delete a file from storage. You must handle the delete logic within this method.
|
||||
|
||||
This method accepts one parameter, which is an object that holds a `fileKey` property. The value of this property is a string that acts as an identifier of the file to delete. Typically, this would be the returned `key` property by other methods, such as the `upload` method.
|
||||
|
||||
For example, for local file service, it could be the file name.
|
||||
|
||||
You can also pass custom properties to the object passed as a first parameter.
|
||||
|
||||
This method is not expected to return anything.
|
||||
|
||||
An example implementation of this method for the local file service:
|
||||
|
||||
```ts title="src/services/local-file.ts"
|
||||
class LocalFileService extends AbstractFileService {
|
||||
|
||||
async delete(
|
||||
fileData: DeleteFileType
|
||||
): Promise<void> {
|
||||
fs.rmSync(fileData.fileKey)
|
||||
}
|
||||
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### getUploadStreamDescriptor
|
||||
|
||||
This method is used to upload a file using a write stream. This is useful if the file is being written through a stream rather than uploaded to the temporary directory.
|
||||
|
||||
The method accepts one parameter, which is an object that has the following properties:
|
||||
|
||||
- `name`: a string indicating the name of the file.
|
||||
- `ext`: an optional string indicating the extension of the file.
|
||||
- `isPrivate`: an optional boolean value indicating if the file should be uploaded to a private bucket or location. By convention, the default value of this property should be `true`
|
||||
|
||||
The method is expected to return an object having the following properties:
|
||||
|
||||
- `writeStream`: a write stream object.
|
||||
- `promise`: A promise that should resolved when the writing process is done to finish the upload. This depends on the type of file service you’re creating.
|
||||
- `url`: a string indicating the URL of the file once it’s uploaded.
|
||||
- `fileKey`: a string indicating the identifier of your file in the storage. For example, for a local file service this can be the file name.
|
||||
|
||||
You can also return custom properties within the object.
|
||||
|
||||
An example implementation of this method for the local file service:
|
||||
|
||||
```ts title="src/services/local-file.ts"
|
||||
class LocalFileService extends AbstractFileService {
|
||||
|
||||
async getUploadStreamDescriptor({
|
||||
name,
|
||||
ext,
|
||||
isPrivate = true,
|
||||
}: UploadStreamDescriptorType
|
||||
): Promise<FileServiceGetUploadStreamResult> {
|
||||
const filePath = `${isPrivate ?
|
||||
this.publicPath : this.protectedPath
|
||||
}/${name}.${ext}`
|
||||
const writeStream = fs.createWriteStream(filePath)
|
||||
|
||||
return {
|
||||
writeStream,
|
||||
promise: Promise.resolve(),
|
||||
url: `${this.serverUrl}/${filePath}`,
|
||||
fileKey: filePath,
|
||||
}
|
||||
}
|
||||
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### getDownloadStream
|
||||
|
||||
This method is used to read a file using a read stream, typically for download.
|
||||
|
||||
The method accepts as a parameter an object having the following properties:
|
||||
|
||||
- `fileKey`: a string indicating the identifier of the file. This is typically the value returned by other methods like [upload](#upload).
|
||||
- `isPrivate`: an optional boolean value indicating whether the file should be downloaded from the private bucket or storage location. By convention, this should default to `true`.
|
||||
|
||||
The method is expected to return a readable stream.
|
||||
|
||||
An example implementation of this method for the local file service:
|
||||
|
||||
```ts title="src/services/local-file.ts"
|
||||
class LocalFileService extends AbstractFileService {
|
||||
|
||||
async getDownloadStream({
|
||||
fileKey,
|
||||
isPrivate = true,
|
||||
}: GetUploadedFileType
|
||||
): Promise<NodeJS.ReadableStream> {
|
||||
const filePath = `${isPrivate ?
|
||||
this.publicPath : this.protectedPath
|
||||
}/${fileKey}`
|
||||
const readStream = fs.createReadStream(filePath)
|
||||
|
||||
return readStream
|
||||
}
|
||||
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### getPresignedDownloadUrl
|
||||
|
||||
The `getPresignedDownloadUrl` method is used to retrieve a download URL of the file. For some file services, such as S3, a presigned URL indicates a temporary URL to get access to a file.
|
||||
|
||||
If your file service doesn’t perform or offer a similar functionality, you can just return the URL to download the file.
|
||||
|
||||
The method accepts as a parameter an object having the following properties:
|
||||
|
||||
- `fileKey`: a string indicating the identifier of the file. This is typically the value returned by other methods like [upload](#upload).
|
||||
- `isPrivate`: an optional boolean value indicating whether the file should be downloaded from the private bucket or storage location. By convention, this should default to `true`.
|
||||
|
||||
The method is expected to return a string, being the URL of the file.
|
||||
|
||||
An example implementation of this method for the local file service:
|
||||
|
||||
```ts title="src/services/local-file.ts"
|
||||
class LocalFileService extends AbstractFileService {
|
||||
|
||||
async getPresignedDownloadUrl({
|
||||
fileKey,
|
||||
isPrivate = true,
|
||||
}: GetUploadedFileType
|
||||
): Promise<string> {
|
||||
const filePath = `${isPrivate ?
|
||||
this.publicPath : this.protectedPath
|
||||
}/${fileKey}`
|
||||
return `${this.serverUrl}/${filePath}`
|
||||
}
|
||||
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 3: Run Build Command
|
||||
|
||||
In the directory of the Medusa backend, run the `build` command to transpile the files in the `src` directory into the `dist` directory:
|
||||
|
||||
```bash npm2yarn
|
||||
npm run build
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Test it Out
|
||||
|
||||
:::note
|
||||
|
||||
This section explains how to test out your implementation if the file service was created in the Medusa backend codebase. You can refer to the [plugin documentation](../plugins/create.mdx#test-your-plugin) on how to test a plugin.
|
||||
|
||||
:::
|
||||
|
||||
Run your backend to test it out:
|
||||
|
||||
```bash npm2yarn
|
||||
npx medusa develop
|
||||
```
|
||||
|
||||
Then, try uploading a file, for example, using the [Upload File API Route](https://docs.medusajs.com/api/admin#uploads_postuploads). The file should be uploaded based on the logic you’ve implemented.
|
||||
|
||||
### (Optional) Accessing the File
|
||||
|
||||
:::note
|
||||
|
||||
This step is only useful if you're implementing a local file service.
|
||||
|
||||
:::
|
||||
|
||||
Since the file is uploaded to a local directory `uploads`, you need to configure a static route in express that allows accessing the files within the `uploads` directory.
|
||||
|
||||
To do that, create the file `src/api/index.ts` with the following content:
|
||||
|
||||
```ts title="src/api/middlewares.ts"
|
||||
import type { MiddlewaresConfig } from "@medusajs/medusa"
|
||||
import express from "express"
|
||||
|
||||
export const config: MiddlewaresConfig = {
|
||||
routes: [
|
||||
{
|
||||
matcher: "/uploads",
|
||||
middlewares: [express.static(uploadDir)],
|
||||
},
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## See Also
|
||||
|
||||
- [How to create a plugin](../plugins/create.mdx)
|
||||
- [How to publish a plugin](../plugins/publish.mdx)
|
||||
@@ -1,340 +0,0 @@
|
||||
---
|
||||
description: 'Learn how to create a notification provider in Medusa. This guide explains the different methods available in a Notification provider.'
|
||||
addHowToData: true
|
||||
---
|
||||
|
||||
# How to Create a Notification Provider
|
||||
|
||||
In this document, you’ll learn how to create a Notification Provider in Medusa.
|
||||
|
||||
:::note
|
||||
|
||||
If you’re unfamiliar with the Notification architecture in Medusa, it is recommended to check out the [architecture overview](./overview.mdx) first.
|
||||
|
||||
:::
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Before you start creating a Notification Provider, you need to either install a [Medusa backend](../backend/install.mdx), or create it in a [plugin](../plugins/overview.mdx). The Medusa backend must also have an event bus module installed, which is available when using the default Medusa backend starter.
|
||||
|
||||
---
|
||||
|
||||
## Create a Notification Provider
|
||||
|
||||
Creating a Notification Provider is as simple as creating a TypeScript or JavaScript file in `src/services`. The name of the file is the name of the provider (for example, `sendgrid.ts`). A Notification Provider is essentially a Service that extends the `AbstractNotificationService` from `@medusajs/medusa`.
|
||||
|
||||
For example, create the file `src/services/email-sender.ts` with the following content:
|
||||
|
||||
```ts title="src/services/email-sender.ts"
|
||||
import { AbstractNotificationService } from "@medusajs/medusa"
|
||||
import { EntityManager } from "typeorm"
|
||||
|
||||
class EmailSenderService extends AbstractNotificationService {
|
||||
protected manager_: EntityManager
|
||||
protected transactionManager_: EntityManager
|
||||
|
||||
sendNotification(
|
||||
event: string,
|
||||
data: unknown,
|
||||
attachmentGenerator: unknown
|
||||
): Promise<{
|
||||
to: string;
|
||||
status: string;
|
||||
data: Record<string, unknown>;
|
||||
}> {
|
||||
throw new Error("Method not implemented.")
|
||||
}
|
||||
resendNotification(
|
||||
notification: unknown,
|
||||
config: unknown,
|
||||
attachmentGenerator: unknown
|
||||
): Promise<{
|
||||
to: string;
|
||||
status: string;
|
||||
data: Record<string, unknown>;
|
||||
}> {
|
||||
throw new Error("Method not implemented.")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default EmailSenderService
|
||||
```
|
||||
|
||||
Where `EmailSenderService` is the name of your Notification Provider Service.
|
||||
|
||||
Notification Providers must extend `NotificationService` from `medusa-interfaces`.
|
||||
|
||||
:::info
|
||||
|
||||
Following the naming convention of Services, the name of the file should be the slug name of the Notification Provider, and the name of the class should be the camel case name of the Notification Provider suffixed with “Service”. In the example above, the name of the file should be `email-sender.js`. You can learn more in the [service documentation](../services/create-service.mdx).
|
||||
|
||||
:::
|
||||
|
||||
### identifier
|
||||
|
||||
Notification Provider Services must have a static property `identifier`.
|
||||
|
||||
The `NotificationProvider` entity has 2 properties: `identifier` and `is_installed`. The value of the `identifier` property in the Service class is used when the Notification Provider is created in the database.
|
||||
|
||||
The value of this property is also used later when you want to subscribe the Notification Provider to events in a Loader.
|
||||
|
||||
For example, in the class you created in the previous code snippet you can add the following property:
|
||||
|
||||
```ts
|
||||
class EmailSenderService extends AbstractNotificationService {
|
||||
static identifier = "email-sender"
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### constructor
|
||||
|
||||
You can use the `constructor` of your Notification Provider to have access to different Services in Medusa through dependency injection.
|
||||
|
||||
You can also use the constructor to initialize your integration with the third-party provider. For example, if you use a client to connect to the third-party provider’s APIs, you can initialize it in the constructor and use it in other methods in the Service.
|
||||
|
||||
Additionally, if you’re creating your Notification Provider as an external plugin to be installed on any Medusa backend and you want to access the options added for the plugin, you can access it in the constructor. The options are passed as a second parameter.
|
||||
|
||||
:::info
|
||||
|
||||
You can learn more about plugins and how to create them in the [Plugins](../plugins/overview.mdx) documentation.
|
||||
|
||||
:::
|
||||
|
||||
Continuing on with the previous example, if you want to use the [`OrderService`](../../references/services/classes/services.OrderService.mdx) later when sending notifications, you can inject it into the constructor:
|
||||
|
||||
```ts
|
||||
import {
|
||||
AbstractNotificationService,
|
||||
OrderService,
|
||||
} from "@medusajs/medusa"
|
||||
|
||||
class EmailSenderService extends AbstractNotificationService {
|
||||
protected manager_: EntityManager
|
||||
protected transactionManager_: EntityManager
|
||||
static identifier = "email-sender"
|
||||
protected orderService: OrderService
|
||||
|
||||
constructor(container, options) {
|
||||
super(container)
|
||||
// you can access options here in case you're
|
||||
// using a plugin
|
||||
|
||||
this.orderService = container.orderService
|
||||
}
|
||||
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### sendNotification
|
||||
|
||||
When an event is triggered that your Notification Provider is registered as a handler for, the [`NotificationService`](../../references/services/classes/services.NotificationService.mdx) in Medusa’s core will execute the `sendNotification` method of your Notification Provider.
|
||||
|
||||
In this method, you can perform the necessary operation to send the Notification. Following the example above, you can send an email to the customer when they place an order.
|
||||
|
||||
This method receives three parameters:
|
||||
|
||||
1. `eventName`: This is the name of the event that was triggered. For example, `order.placed`.
|
||||
2. `eventData`: This is the data payload of the event that was triggered. For example, if the `order.placed` event is triggered, the `eventData` object contains the property `id` which is the ID of the order that was placed.
|
||||
3. `attachmentGenerator`: If you’ve previously attached a generator to the `NotificationService` using the [`registerAttachmentGenerator`](../../references/services/classes/services.NotificationService.mdx#registerattachmentgenerator) method, you have access to it here. You can use the `attachmentGenerator` to generate on-demand invoices or other documents. The default value of this parameter is null.
|
||||
|
||||
:::info
|
||||
|
||||
You can learn more about what events are triggered in Medusa and their data payload in the [Events List](../events/events-list.md) documentation.
|
||||
|
||||
:::
|
||||
|
||||
This method must return an object containing two properties:
|
||||
|
||||
1. `to`: a string that represents the receiver of the Notification. For example, if you sent an email to the customer then `to` is the email address of the customer. In other cases, it might be a phone number or a username.
|
||||
2. `data`: an object that contains the data used to send the Notification. For example, if you sent an order confirmation email to the customer, then the `data` object might include the order items or the subject of the email. This `data` is necessary if the notification is resent later as you can use the same data.
|
||||
|
||||
Continuing with the previous example you can have the following implementation of the `sendNotification` method:
|
||||
|
||||
```ts
|
||||
class EmailSenderService extends AbstractNotificationService {
|
||||
// ...
|
||||
async sendNotification(
|
||||
event: string,
|
||||
data: any,
|
||||
attachmentGenerator: unknown
|
||||
): Promise<{
|
||||
to: string;
|
||||
status: string;
|
||||
data: Record<string, unknown>;
|
||||
}> {
|
||||
if (event === "order.placed") {
|
||||
// retrieve order
|
||||
const order = await this.orderService.retrieve(data.id)
|
||||
// TODO send email
|
||||
|
||||
console.log("Notification sent")
|
||||
return {
|
||||
to: order.email,
|
||||
status: "done",
|
||||
data: {
|
||||
// any data necessary to send the email
|
||||
// for example:
|
||||
subject: "You placed a new order!",
|
||||
items: order.items,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
In this code snippet, you check first if the event is `order.placed`. This can be helpful if you’re handling multiple events using the same Notification Provider.
|
||||
|
||||
You then retrieve the order using the ID and send the email. Here, the logic related to sending the email is not implemented as it is generally specific to your Notification Provider.
|
||||
|
||||
Finally, you return an object with the `to` property set to the customer email and the `data` property is an object that contains data necessary to send the email such as a `subject` or `items`.
|
||||
|
||||
:::note
|
||||
|
||||
The `to` and `data` properties are used in the `NotificationService` in Medusa’s core to create a new `Notification` record in the database. You can learn more about the `Notification` entity in the [Architecture Overview](./overview.mdx#notification-entity-overview) documentation.
|
||||
|
||||
:::
|
||||
|
||||
### resendNotification
|
||||
|
||||
Using the [Resend Notification API Route](https://docs.medusajs.com/api/admin#notifications_postnotificationsnotificationresend), an admin user can resend a Notification to the customer. The [`NotificationService`](../../references/services/classes/services.NotificationService.mdx) in Medusa’s core then executes the `resendNotification` method in your Notification Provider.
|
||||
|
||||
This method receives three parameters:
|
||||
|
||||
1. `notification`: This is the original Notification record that was created after you sent the notification with `sendNotification`. You can get an overview of the entity and its attributes in the [architecture overview](./overview.mdx#notification-entity-overview), but most notably it includes the `to` and `data` attributes which are populated originally using the `to` and `data` properties of the object you return in `sendNotification`.
|
||||
2. `config`: In the Resend Notification API Route you may specify an alternative receiver of the notification using the `to` request body parameter. For example, you may want to resend the order confirmation email to a different email. If that’s the case, you have access to it in the `config` parameter object. Otherwise, `config` will be an empty object.
|
||||
3. `attachmentGenerator`: If you’ve previously attached a generator to the Notification Service using the [`registerAttachmentGenerator`](../../references/services/classes/services.NotificationService.mdx#registerattachmentgenerator) method, you have access to it here. You can use the `attachmentGenerator` to generate on-demand invoices or other documents. The default value of this parameter is null.
|
||||
|
||||
Similarly to the `sendNotification` method, this method must return an object containing two properties:
|
||||
|
||||
1. `to`: a string that represents the receiver of the Notification. You can either return the same `to` available in the `notification` parameter or the updated one in the `config` parameter.
|
||||
2. `data`: an object that contains the data used to send the Notification. You can either return the same `data` in the `notification` parameter or make any necessary updates to it.
|
||||
|
||||
Continuing with the previous example you can have the following implementation of the `resendNotification` method:
|
||||
|
||||
```ts
|
||||
class EmailSenderService extends AbstractNotificationService {
|
||||
// ...
|
||||
async resendNotification(
|
||||
notification: any,
|
||||
config: any,
|
||||
attachmentGenerator: unknown
|
||||
): Promise<{
|
||||
to: string;
|
||||
status: string;
|
||||
data: Record<string, unknown>;
|
||||
}> {
|
||||
// check if the receiver should be changed
|
||||
const to: string = config.to ? config.to : notification.to
|
||||
|
||||
// TODO resend the notification using the same data
|
||||
// that is saved under notification.data
|
||||
|
||||
console.log("Notification resent")
|
||||
return {
|
||||
to,
|
||||
status: "done",
|
||||
data: notification.data, // make changes to the data
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
In the above snippet, you check if the `to` should be changed by checking if the `config` parameter has a `to` property. Otherwise, you keep the same `to` address stored in the `notification` parameter.
|
||||
|
||||
You then resend the email. Here, the logic related to sending the email is not implemented as it is generally specific to your Notification Provider.
|
||||
|
||||
Finally, you return an object with the `to` property set to the email (either new or old) and the `data` property is the same data used before to send the original notification. you can alternatively make any changes to the data.
|
||||
|
||||
:::note
|
||||
|
||||
The `to` and `data` properties are used in the `NotificationService` in Medusa’s core to create a new `Notification` record in the database. No changes are made to the original `Notification` record created after the `sendNotification` method. This new record is associated with the original `Notification` record using the `parent_id` attribute in the entity. You can learn more about the `Notification` entity in the [Architecture Overview](./overview.mdx#notification-entity-overview) documentation.
|
||||
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## Subscribe with Loaders
|
||||
|
||||
After creating your Notification Provider Service, you must create a [Loader](../loaders/overview.mdx) that registers this Service as a notification handler of events.
|
||||
|
||||
Following the previous example, to make sure the `email-sender` Notification Provider handles the `order.placed` event, create the file `src/loaders/notification.ts` with the following content:
|
||||
|
||||
```ts title="src/loaders/notification.ts"
|
||||
import {
|
||||
MedusaContainer,
|
||||
NotificationService,
|
||||
} from "@medusajs/medusa"
|
||||
|
||||
export default async (
|
||||
container: MedusaContainer
|
||||
): Promise<void> => {
|
||||
const notificationService = container.resolve<
|
||||
NotificationService
|
||||
>("notificationService")
|
||||
|
||||
notificationService.subscribe(
|
||||
"order.placed",
|
||||
"email-sender"
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
This loader accesses the `notificationService` through the [MedusaContainer](../fundamentals/dependency-injection.md). The `notificationService` has a `subscribe` method that accepts 2 parameters. The first one is the name of the event to subscribe to, and the second is the identifier of the Notification Provider that's subscribing to that event.
|
||||
|
||||
---
|
||||
|
||||
## Test Sending Notifications with your Notification Provider
|
||||
|
||||
Make sure you have an event bus module configured in your Medusa backend. You can learn more on how to do that in the [Configurations guide](../backend/configurations.md).
|
||||
|
||||
Then, run the build command in the root directory of your Medusa backend:
|
||||
|
||||
```bash npm2yarn
|
||||
npm run build
|
||||
```
|
||||
|
||||
Next, start your Medusa backend:
|
||||
|
||||
```bash npm2yarn
|
||||
npx medusa develop
|
||||
```
|
||||
|
||||
Try now to place an order either using the [REST APIs](https://docs.medusajs.com/api/store) or using the storefront.
|
||||
|
||||
:::tip
|
||||
|
||||
If you don’t have a storefront installed you can get started with the [Next.js Starter Template](../../starters/nextjs-medusa-starter.mdx) in minutes.
|
||||
|
||||
:::
|
||||
|
||||
After placing an order, you can see in your console the message “Notification Sent”. If you added your own notification sending logic, you should receive an email or alternatively the type of notification you’ve set up.
|
||||
|
||||
---
|
||||
|
||||
## Test Resending Notifications with your Notification Provider
|
||||
|
||||
To test resending a notification, first, retrieve the ID of the notification you just sent using the [List Notifications API Route](https://docs.medusajs.com/api/admin#notifications_getnotifications). You can pass as a body parameter the `to` or `event_name` parameters to filter out the notification you just sent.
|
||||
|
||||
:::tip
|
||||
|
||||
You must be authenticated as an admin user before sending this request. You can use the [Authenticate a User API Route](https://docs.medusajs.com/api/admin#auth_postauth) to get authenticated.
|
||||
|
||||
:::
|
||||
|
||||

|
||||
|
||||
Then, send a request to the [Resend Notification API Route](https://docs.medusajs.com/api/admin#notifications_postnotificationsnotificationresend) using the ID retrieved from the previous request. You can pass the `to` parameter in the body to change the receiver of the notification. You should see the message “Notification Resent” in your console and if you implemented your own logic for resending the notification it will be resent.
|
||||
|
||||

|
||||
|
||||
This request returns the same notification object as the List Notifications API Route, but it now has a new object in the `resends` array. This is the resent notification. If you supplied a `to` parameter in the request body, you should see its value in the `to` property of the resent notification object.
|
||||
|
||||
---
|
||||
|
||||
## See Also
|
||||
|
||||
- [Create a Plugin](../plugins/create.mdx)
|
||||
@@ -23,7 +23,7 @@ This guide explains how to create a scheduled job on your Medusa backend. The sc
|
||||
|
||||
## Prerequisites
|
||||
|
||||
To use scheduled jobs, you must configure Redis in your Medusa backend. Learn more in the [Configurations documentation](../backend/configurations.md#redis_url).
|
||||
To use scheduled jobs, you must configure Redis in your Medusa backend. Learn more in the [Configurations documentation](../../references/medusa_config/interfaces/medusa_config.ConfigModule.mdx#redis_url).
|
||||
|
||||
---
|
||||
|
||||
@@ -91,7 +91,7 @@ The function accepts a parameter of type `ScheduledJobArgs`, which has the follo
|
||||
|
||||
- `container`: The [dependency container](../fundamentals/dependency-injection.md) that allows you to resolve Medusa resources, such as services.
|
||||
- `data`: The data passed in the [configuration object](#scheduled-job-configuration).
|
||||
- `pluginOptions`: When the scheduled job is created within a plugin, this object holds the plugin's options defined in the [Medusa configurations](../backend/configurations.md).
|
||||
- `pluginOptions`: When the scheduled job is created within a plugin, this object holds the plugin's options defined in the [Medusa configurations](../../references/medusa_config/interfaces/medusa_config.ConfigModule.mdx).
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ Scheduled jobs (also known as cron jobs) are tasks performed at a specific time
|
||||
|
||||
For example, you can synchronize your inventory with an Enterprise Resource Planning (ERP) system once a day using a scheduled job.
|
||||
|
||||
In the Medusa Backend, the scheduled jobs queue is implemented using [Redis](https://redis.io/) and [Bull](https://www.npmjs.com/package/bull). So, for scheduled jobs to work, you must have [Redis installed and enabled](../../development/backend/configurations.md#redis).
|
||||
In the Medusa Backend, the scheduled jobs queue is implemented using [Redis](https://redis.io/) and [Bull](https://www.npmjs.com/package/bull). So, for scheduled jobs to work, you must have [Redis installed and enabled](../../references/medusa_config/interfaces/medusa_config.ConfigModule.mdx#redis_url).
|
||||
|
||||
:::tip
|
||||
|
||||
|
||||
@@ -1,416 +0,0 @@
|
||||
---
|
||||
description: 'Learn how to create a search service in Medusa. You can create the search service directly in your Medusa backend codebase, in a plugin, or in a module.'
|
||||
addHowToData: true
|
||||
---
|
||||
|
||||
# How to Create a Search Service
|
||||
|
||||
In this document, you’ll learn how to create a search service in Medusa. You can create the search service directly in your Medusa backend codebase, in a plugin, or in a module.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
### Medusa Utils Package
|
||||
|
||||
A search service must extend the `AbstractSearchService` class imported from the `@medusajs/utils` package. If you don’t already have the package installed, run the following command to install it within your project:
|
||||
|
||||
```bash npm2yarn
|
||||
npm install @medusajs/utils
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 1: Create Search Service Class
|
||||
|
||||
A search service class should be defined in a TypeScript or JavaScript file created in the `src/services` directory. The class must extend the `AbstractSearchService` class imported from the `@medusajs/utils` package.
|
||||
|
||||
Based on services’ naming conventions, the file’s name should be the slug version of the search service’s name without `service`, and the class’s name should be the pascal case of the search service’s name following by `Service`.
|
||||
|
||||
For example, if you’re creating an algolia search service, the file name would be `algolia.ts`, whereas the class name would be `AlgoliaService`.
|
||||
|
||||
:::note
|
||||
|
||||
You can learn more about services and their naming convention in [this documentation](../services/overview.mdx).
|
||||
|
||||
:::
|
||||
|
||||
For example, create the file `src/services/my-search.ts` with the following content:
|
||||
|
||||
```ts title="src/services/my-search.ts"
|
||||
import { AbstractSearchService } from "@medusajs/utils"
|
||||
|
||||
class MySearchService extends AbstractSearchService {
|
||||
isDefault = false
|
||||
|
||||
createIndex(indexName: string, options: Record<string, any>) {
|
||||
throw new Error("Method not implemented.")
|
||||
}
|
||||
getIndex(indexName: string) {
|
||||
throw new Error("Method not implemented.")
|
||||
}
|
||||
addDocuments(
|
||||
indexName: string,
|
||||
documents: Record<string, any>[],
|
||||
type: string
|
||||
) {
|
||||
throw new Error("Method not implemented.")
|
||||
}
|
||||
replaceDocuments(
|
||||
indexName: string,
|
||||
documents: Record<string, any>[],
|
||||
type: string
|
||||
) {
|
||||
throw new Error("Method not implemented.")
|
||||
}
|
||||
deleteDocument(
|
||||
indexName: string,
|
||||
document_id: string | number
|
||||
) {
|
||||
throw new Error("Method not implemented.")
|
||||
}
|
||||
deleteAllDocuments(indexName: string) {
|
||||
throw new Error("Method not implemented.")
|
||||
}
|
||||
search(
|
||||
indexName: string,
|
||||
query: string,
|
||||
options: Record<string, any>
|
||||
) {
|
||||
return {
|
||||
message: "test",
|
||||
}
|
||||
}
|
||||
updateSettings(
|
||||
indexName: string,
|
||||
settings: Record<string, any>
|
||||
) {
|
||||
throw new Error("Method not implemented.")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default MySearchService
|
||||
```
|
||||
|
||||
This creates the service `MySearchService` which, at the moment, adds a general implementation of the methods defined in the abstract class `AbstractSearchService`.
|
||||
|
||||
### Using a Constructor
|
||||
|
||||
You can use a constructor to access services and resources registered in the dependency container, to define any necessary clients if you’re integrating a third-party storage service, and to access plugin options if your search service is defined in a plugin.
|
||||
|
||||
For example:
|
||||
|
||||
<!-- eslint-disable prefer-rest-params -->
|
||||
|
||||
```ts title="src/services/my-search.ts"
|
||||
// ...
|
||||
import { ProductService } from "@medusajs/medusa"
|
||||
|
||||
type InjectedDependencies = {
|
||||
productService: ProductService
|
||||
}
|
||||
|
||||
class MySearchService extends AbstractSearchService {
|
||||
// ...
|
||||
protected readonly productService_: ProductService
|
||||
|
||||
constructor({ productService }: InjectedDependencies) {
|
||||
// @ts-expect-error prefer-rest-params
|
||||
super(...arguments)
|
||||
this.productService_ = productService
|
||||
}
|
||||
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
You can access the plugin options in the second parameter passed to the constructor:
|
||||
|
||||
<!-- eslint-disable prefer-rest-params -->
|
||||
|
||||
```ts title="src/services/my-search.ts"
|
||||
// ...
|
||||
|
||||
class MySearchService extends AbstractSearchService {
|
||||
// ...
|
||||
protected readonly pluginOptions: Record<string, any>
|
||||
|
||||
constructor({
|
||||
productService,
|
||||
}: InjectedDependencies, pluginOptions) {
|
||||
// @ts-expect-error prefer-rest-params
|
||||
super(...arguments)
|
||||
|
||||
// ...
|
||||
this.pluginOptions = pluginOptions
|
||||
}
|
||||
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### isDefault Property
|
||||
|
||||
The `isDefault` property is mainly used to pinpoint the default search service defined in the Medusa core. For custom search services, the `isDefault` property should be `false`.
|
||||
|
||||
---
|
||||
|
||||
## Step 2: Implement Required Methods
|
||||
|
||||
In this section, you’ll learn about the required methods to implement in the search service.
|
||||
|
||||
:::note
|
||||
|
||||
The Medusa backend mainly uses the `addDocuments`, `deleteDocument`, and `search` methods in different scenarios that are explained for each of the methods. Other methods can be helpful based on the search engine you’re integrating.
|
||||
|
||||
:::
|
||||
|
||||
### createIndex
|
||||
|
||||
This method is used to create an index in the search engine.
|
||||
|
||||
The method accepts two parameters:
|
||||
|
||||
1. `indexName`: this is the first parameter, and it’s a string indicating the name of the index to create.
|
||||
2. `options`: this is the second parameter is typically an object, and it can be used to pass any necessary options to the method. This parameter does not have any defined format.
|
||||
|
||||
The method does not require any specific data type to be returned.
|
||||
|
||||
An example implementation, assuming `client_` would interact with a third-party service:
|
||||
|
||||
```ts title="src/services/my-search.ts"
|
||||
class MySearchService extends AbstractSearchService {
|
||||
// ...
|
||||
|
||||
createIndex(indexName: string, options: Record<string, any>) {
|
||||
return this.client_.initIndex(indexName)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### getIndex
|
||||
|
||||
This method is used to retrieve an index’s results from the search engine.
|
||||
|
||||
The method accepts one parameter, which is a string indicating the name of the index. The method does not require any specific data type to be returned.
|
||||
|
||||
An example implementation, assuming `client_` would interact with a third-party service:
|
||||
|
||||
```ts title="src/services/my-search.ts"
|
||||
class MySearchService extends AbstractSearchService {
|
||||
// ...
|
||||
|
||||
getIndex(indexName: string) {
|
||||
return this.client_.getIndex(indexName)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### addDocuments
|
||||
|
||||
This method is used to add a document to an index in the search engine.
|
||||
|
||||
This method is used when the Medusa backend loads, indexing all products available in the Medusa backend. It’s also used whenever a new product is added or a product is updated.
|
||||
|
||||
The method accepts the following parameters:
|
||||
|
||||
- `indexName`: the first parameter is a string indicating the name of the index to add the document to.
|
||||
- `documents`: the second parameter is typically an array of objects to index. For example, it can be an array of products to index.
|
||||
- `type`: the third parameter is a string indicating the type of object being indexed. For example, when indexing products, the type would be `products`.
|
||||
|
||||
The method should return the response of saving the documents in the search engine, but there’s no required format of the response.
|
||||
|
||||
An example implementation, assuming `client_` would interact with a third-party service:
|
||||
|
||||
```ts title="src/services/my-search.ts"
|
||||
class MySearchService extends AbstractSearchService {
|
||||
// ...
|
||||
|
||||
async addDocuments(
|
||||
indexName: string,
|
||||
documents: Record<string, any>[],
|
||||
type: string
|
||||
) {
|
||||
return await this.client_
|
||||
.addDocuments(indexName, documents)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### replaceDocuments
|
||||
|
||||
This method is used to replace the existing documents in the search engine of an index with new documents.
|
||||
|
||||
The method accepts the following parameters:
|
||||
|
||||
- `indexName`: the first parameter is a string indicating the name of the index to replace the documents in.
|
||||
- `documents`: the second parameter is typically an array of objects to index. For example, it can be an array of products to index. This would be the new documents to add to the index.
|
||||
- `type`: the third parameter is a string indicating the type of object being indexed. For example, when indexing products, the type would be `products`.
|
||||
|
||||
The method should return the response of saving the documents in the search engine, but there’s no required format of the response.
|
||||
|
||||
An example implementation, assuming `client_` would interact with a third-party service:
|
||||
|
||||
```ts title="src/services/my-search.ts"
|
||||
class MySearchService extends AbstractSearchService {
|
||||
// ...
|
||||
|
||||
async replaceDocuments(
|
||||
indexName: string,
|
||||
documents: Record<string, any>[],
|
||||
type: string
|
||||
) {
|
||||
await this.client_
|
||||
.removeDocuments(indexName)
|
||||
return await this.client_
|
||||
.addDocuments(indexName, documents)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### deleteDocument
|
||||
|
||||
This method is used to delete a document from an index.
|
||||
|
||||
When a product is deleted in the Medusa backend, this method is used to delete the product from the search engine’s index.
|
||||
|
||||
The method accepts the following parameters:
|
||||
|
||||
- `indexName`: the first parameter is a string indicating the name of the index this document belongs in.
|
||||
- `document_id`: the second parameter is a string or a number indicating the ID of the document to delete. When a product is deleted, the product’s ID is passed as the value of this parameter.
|
||||
|
||||
The method should return the response of deleting the document in the search engine, but there’s no required format of the response.
|
||||
|
||||
An example implementation, assuming `client_` would interact with a third-party service:
|
||||
|
||||
```ts title="src/services/my-search.ts"
|
||||
class MySearchService extends AbstractSearchService {
|
||||
// ...
|
||||
|
||||
async deleteDocument(
|
||||
indexName: string,
|
||||
document_id: string | number
|
||||
) {
|
||||
return await this.client_
|
||||
.deleteDocument(indexName, document_id)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### deleteAllDocuments
|
||||
|
||||
This method is used to delete all documents from an index.
|
||||
|
||||
The method accepts one parameter, which is a string indicating the name of the index to delete its documents.
|
||||
|
||||
The method should return the response of deleting the documents of that index in the search engine, but there’s no required format of the response.
|
||||
|
||||
An example implementation, assuming `client_` would interact with a third-party service:
|
||||
|
||||
```ts title="src/services/my-search.ts"
|
||||
class MySearchService extends AbstractSearchService {
|
||||
// ...
|
||||
|
||||
async deleteAllDocuments(indexName: string) {
|
||||
return await this.client_
|
||||
.deleteDocuments(indexName)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### search
|
||||
|
||||
This method is used to search through an index by a query.
|
||||
|
||||
In the Medusa backend, this method is used within the [Search Products API Route](https://docs.medusajs.com/api/store#products_postproductssearch) to retrieve the search results.
|
||||
|
||||
This method accepts the following parameters:
|
||||
|
||||
1. `indexName`: the first parameter is a string indicating the index to search through. When using the Search Products API Route, the index is the default index defined in the `IndexName` static property of the `ProductService`, which is `products`.
|
||||
2. `query`: the second parameter is a string indicating the query to use to search through the documents.
|
||||
3. `options`: the third parameter is typically an object that can be used to pass any necessary options to the search engine.
|
||||
|
||||
Although there’s no required data format or type to be returned to the method, it’s recommended to return an object having a property `hits` with its value being an array of results. Each result can be an object of any format. This is recommended as this is the convention followed within Medusa’s official search plugins.
|
||||
|
||||
An example implementation, assuming `client_` would interact with a third-party service:
|
||||
|
||||
```ts title="src/services/my-search.ts"
|
||||
class MySearchService extends AbstractSearchService {
|
||||
// ...
|
||||
|
||||
async search(
|
||||
indexName: string,
|
||||
query: string,
|
||||
options: Record<string, any>
|
||||
) {
|
||||
const hits = await this.client_
|
||||
.search(indexName, query)
|
||||
return {
|
||||
hits,
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### updateSettings
|
||||
|
||||
This method is used to update the settings of an index within the search engine. This can be useful if you want to update the index settings when the plugin options change.
|
||||
|
||||
For example, in the Algolia plugin, a loader, which runs when the Medusa backend loads, is used to update the settings of indices based on the plugin options. The loader uses this method to update the settings.
|
||||
|
||||
The method accepts the following parameters:
|
||||
|
||||
1. `indexName`: the first parameter is a string indicating the index that should be updated.
|
||||
2. `settings`: the second parameter is typically an object that holds the settings of the index. There’s no defined format for this parameter.
|
||||
|
||||
The method should return the response of updating the index in the search engine, but there’s no required format of the response.
|
||||
|
||||
An example implementation, assuming `client_` would interact with a third-party service:
|
||||
|
||||
```ts title="src/services/my-search.ts"
|
||||
class MySearchService extends AbstractSearchService {
|
||||
// ...
|
||||
|
||||
async updateSettings(
|
||||
indexName: string,
|
||||
settings: Record<string, any>
|
||||
) {
|
||||
return await this.client_
|
||||
.updateSettings(indexName, settings)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Step 3: Run Build Command
|
||||
|
||||
In the directory of the Medusa backend, run the build command to transpile the files in the `src` directory into the `dist` directory:
|
||||
|
||||
```bash npm2yarn
|
||||
npm run build
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Test it Out
|
||||
|
||||
:::note
|
||||
|
||||
This section explains how to test out your implementation if the search service was created in the Medusa backend codebase. You can refer to [the plugin documentation](../plugins/create.mdx#test-your-plugin) on how to test a plugin.
|
||||
|
||||
:::
|
||||
|
||||
Run your backend to test it out:
|
||||
|
||||
```bash npm2yarn
|
||||
npx medusa develop
|
||||
```
|
||||
|
||||
You can then send a request to the [Search Products API Route](https://docs.medusajs.com/api/store#products_postproductssearch) to see if your search service returns any results.
|
||||
|
||||
---
|
||||
|
||||
## See Also
|
||||
|
||||
- [How to create a plugin](../plugins/create.mdx)
|
||||
- [How to publish a plugin](../plugins/publish.mdx)
|
||||
@@ -36,8 +36,8 @@ This guide explains how to override a strategy in general. It doesn’t explain
|
||||
There are other resources that provide steps specific to a strategy type:
|
||||
|
||||
- [How to override the Cart Completion Strategy](../../modules/carts-and-checkout/backend/cart-completion-strategy.md)
|
||||
- [How to override the Tax Calculation Strategy](../../modules/taxes/backend/tax-calculation-strategy.md)
|
||||
- [How to override the Price Selection Strategy](../../modules/price-lists/backend/override-price-selection-strategy.mdx)
|
||||
- [How to override the Tax Calculation Strategy](../../references/tax_calculation/classes/tax_calculation.AbstractTaxCalculationStrategy.mdx)
|
||||
- [How to override the Price Selection Strategy](/modules/price-lists/price-selection-strategy)
|
||||
- [How to override a Batch Job Strategy](../batch-jobs/customize-import.md)
|
||||
|
||||
---
|
||||
|
||||
Reference in New Issue
Block a user