docs: update endpoints to use file-routing approach (#5397)
- Move the original guides for creating endpoints and middlewares to sub-sections in the Endpoints category. - Replace existing guides for endpoints and middlewares with the new approach. - Update all endpoints-related snippets across docs to use this new approach.
This commit is contained in:
+7
-2
@@ -1,5 +1,4 @@
|
||||
---
|
||||
description: 'Learn how to add a middleware in Medusa. A middleware is a function that has access to the request and response objects and can be used to perform actions around an endpoint.'
|
||||
addHowToData: true
|
||||
---
|
||||
|
||||
@@ -7,10 +6,16 @@ import Troubleshooting from '@site/src/components/Troubleshooting'
|
||||
import ServiceLifetimeSection from '../../troubleshooting/awilix-resolution-error/_service-lifetime.md'
|
||||
import CustomRegistrationSection from '../../troubleshooting/awilix-resolution-error/_custom-registration.md'
|
||||
|
||||
# Middlewares
|
||||
# Middlewares - Express Endpoints Approach
|
||||
|
||||
In this document, you’ll learn how to add a middleware to an existing or custom route in Medusa.
|
||||
|
||||
:::note
|
||||
|
||||
Following v1.17.2 of `@medusajs/medusa`, it's highly recommended to use the [middlewares.ts file](./add-middleware.mdx) to create middlewares instead. Future versions of Medusa may drop support of this approach.
|
||||
|
||||
:::
|
||||
|
||||
## Overview
|
||||
|
||||
As the Medusa backend is built on top of [Express](https://expressjs.com/), Express’s features can be utilized during your development with Medusa.
|
||||
@@ -0,0 +1,214 @@
|
||||
---
|
||||
addHowToData: true
|
||||
---
|
||||
|
||||
import Troubleshooting from '@site/src/components/Troubleshooting'
|
||||
import ServiceLifetimeSection from '../../troubleshooting/awilix-resolution-error/_service-lifetime.md'
|
||||
import CustomRegistrationSection from '../../troubleshooting/awilix-resolution-error/_custom-registration.md'
|
||||
|
||||
# Middlewares
|
||||
|
||||
In this document, you’ll learn how to add a middleware to existing or custom API Routes in Medusa.
|
||||
|
||||
:::tip
|
||||
|
||||
v1.17.2 of `@medusajs/medusa` introduced a new approach to creating middlewares using a single `middlewares.ts` file. You can still use the [Express Router Approach](./add-middleware-express-route.mdx), however, it's highly recommended that you start using this new approach.
|
||||
|
||||
:::
|
||||
|
||||
## Basic Implementation
|
||||
|
||||
```ts title=src/api/middlewares.ts
|
||||
import type { MiddlewaresConfig } from "@medusajs/medusa"
|
||||
import type {
|
||||
MedusaNextFunction,
|
||||
MedusaRequest,
|
||||
MedusaResponse,
|
||||
} from "@medusajs/medusa"
|
||||
|
||||
const storeMiddleware = (
|
||||
req: MedusaRequest,
|
||||
res: MedusaResponse,
|
||||
next: MedusaNextFunction
|
||||
) => {
|
||||
// do something
|
||||
next()
|
||||
}
|
||||
|
||||
export const config: MiddlewaresConfig = {
|
||||
routes: [
|
||||
{
|
||||
matcher: "/store/*",
|
||||
middlewares: [storeMiddleware],
|
||||
},
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
A middleware is a function that has access to the `MedusaRequest` and `MedusaResponse` objects that are passed to API Route method handlers.
|
||||
|
||||
Middlewares are used to perform an action when a request is sent to an API Route, or modify the response of an API route, among other usages.
|
||||
|
||||
### middlewares.ts File
|
||||
|
||||
Middlewares are defined in the `src/api/middlewares.ts` file. This file must expose a config object of type `MiddlewaresConfig` imported from `@medusajs/medusa`.
|
||||
|
||||
This object accepts a parameter `routes`, whose value is an array of middleware route objects. Each middleware route object accepts the following properties:
|
||||
|
||||
- The `matcher` property accepts a string or a regular expression that will be used to check whether the middlewares should be applied on a API Routes.
|
||||
- The `middlewares` property is an array of middlewares that should be applied on API Routes matching the pattern specified in `matcher`.
|
||||
|
||||
---
|
||||
|
||||
## Build Files
|
||||
|
||||
Similar to custom API Routes, you must transpile the files under `src` into the `dist` directory for the backend to load them.
|
||||
|
||||
To do that, run the following command before running the Medusa backend:
|
||||
|
||||
```bash npm2yarn
|
||||
npm run build
|
||||
```
|
||||
|
||||
You can then test that the middleware is working by running the backend.
|
||||
|
||||
---
|
||||
|
||||
## Register New Resources in Dependency Container
|
||||
|
||||
In some cases, you may need to register a resource to use within your commerce application. For example, you may want to register the logged-in user to access it in other resources, such as services. You can do that in your middleware.
|
||||
|
||||
:::tip
|
||||
|
||||
If you want to register a logged-in user and access it in your resources, you can check out [this example guide](./example-logged-in-user.mdx).
|
||||
|
||||
:::
|
||||
|
||||
To register a new resource in the dependency container, use the `MedusaRequest` object's `scope.register` method. It accepts an object, where each key is the name to be registered in the dependency container, and its value is an object that has a `resolve` property.
|
||||
|
||||
The `resolve`'s value is a function that returns the resource to be registered in the dependency container.
|
||||
|
||||
For example:
|
||||
|
||||
```ts title=src/api/middlewares.ts
|
||||
import type { MiddlewaresConfig } from "@medusajs/medusa"
|
||||
import type {
|
||||
MedusaNextFunction,
|
||||
MedusaRequest,
|
||||
MedusaResponse,
|
||||
} from "@medusajs/medusa"
|
||||
|
||||
const customResource = (
|
||||
req: MedusaRequest,
|
||||
res: MedusaResponse,
|
||||
next: MedusaNextFunction) => {
|
||||
req.scope.register({
|
||||
customResource: {
|
||||
resolve: () => "my custom resource",
|
||||
},
|
||||
})
|
||||
|
||||
next()
|
||||
}
|
||||
|
||||
export const config: MiddlewaresConfig = {
|
||||
routes: [
|
||||
{
|
||||
matcher: "/store/*",
|
||||
middlewares: [customResource],
|
||||
},
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
You can then load this new resource within other resources. For example, to load it in a service:
|
||||
|
||||
<!-- eslint-disable prefer-rest-params -->
|
||||
|
||||
```ts title=src/services/custom-service.ts
|
||||
import { TransactionBaseService } from "@medusajs/medusa"
|
||||
|
||||
class CustomService extends TransactionBaseService {
|
||||
|
||||
constructor(container, options) {
|
||||
super(...arguments)
|
||||
|
||||
// use the registered resource.
|
||||
try {
|
||||
container.customResource
|
||||
} catch (e) {
|
||||
// avoid errors when the backend first loads
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default CustomService
|
||||
```
|
||||
|
||||
:::note
|
||||
|
||||
Make sure to wrap your usage of the new resource in a try-catch block when you use it in a constructor. This is to avoid errors that can arise when the backend first loads, as the resource isn't registered yet.
|
||||
|
||||
:::
|
||||
|
||||
### Note About Services Lifetime
|
||||
|
||||
If you want to access new registrations in the dependency container within a service, you must set the lifetime of the service either to `Lifetime.SCOPED` or `Lifetime.TRANSIENT`. Services that have a `Lifetime.SINGLETON` lifetime can't access new registrations since they're resolved and cached in the root dependency container beforehand. You can learn more in the [Create Services documentation](../services/create-service.mdx#service-life-time).
|
||||
|
||||
For custom services, no additional action is required as the default lifetime is `Lifetime.SCOPED`. However, if you extend a core service, you must change the lifetime since the default lifetime for core services is `Lifetime.SINGLETON`.
|
||||
|
||||
For example:
|
||||
|
||||
<!-- eslint-disable prefer-rest-params -->
|
||||
|
||||
```ts
|
||||
import { Lifetime } from "awilix"
|
||||
import {
|
||||
ProductService as MedusaProductService,
|
||||
} from "@medusajs/medusa"
|
||||
|
||||
// extending ProductService from the core
|
||||
class ProductService extends MedusaProductService {
|
||||
// The default life time for a core service is SINGLETON
|
||||
static LIFE_TIME = Lifetime.SCOPED
|
||||
|
||||
constructor(container, options) {
|
||||
super(...arguments)
|
||||
|
||||
// use the registered resource.
|
||||
try {
|
||||
container.customResource
|
||||
} catch (e) {
|
||||
// avoid errors when the backend first loads
|
||||
}
|
||||
}
|
||||
|
||||
// ...
|
||||
}
|
||||
|
||||
export default ProductService
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
<Troubleshooting
|
||||
sections={[
|
||||
{
|
||||
title: 'AwilixResolutionError: Could Not Resolve X',
|
||||
content: <ServiceLifetimeSection />
|
||||
},
|
||||
{
|
||||
title: 'AwilixResolutionError: Could Not Resolve X (Custom Registration)',
|
||||
content: <CustomRegistrationSection />
|
||||
}
|
||||
]}
|
||||
/>
|
||||
|
||||
---
|
||||
|
||||
## See Also
|
||||
|
||||
- [Store API reference](https://docs.medusajs.com/api/store)
|
||||
- [Admin API reference](https://docs.medusajs.com/api/admin)
|
||||
+7
-10
@@ -1,22 +1,19 @@
|
||||
---
|
||||
description: 'Learn how to create endpoints in Medusa. This guide also includes how to add CORS configurations, creating multiple endpoints, adding protected routes, and more.'
|
||||
addHowToData: true
|
||||
---
|
||||
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
|
||||
# How to Create Endpoints
|
||||
# Create Express Endpoint
|
||||
|
||||
In this document, you’ll learn how to create endpoints in Medusa.
|
||||
In this document, you’ll learn how to create express endpoints in Medusa.
|
||||
|
||||
## Overview
|
||||
:::note
|
||||
|
||||
Custom endpoints are created under the `src/api` directory in your Medusa Backend. They're defined in a TypeScript or JavaScript file named `index` (for example, `index.ts`). This file should export a function that returns an Express router or an array of routes and middlewares.
|
||||
Following v1.17.2 of `@medusajs/medusa`, it's highly recommended to use the [API Routes](./create.mdx) instead. Future versions of Medusa may drop support of Express Endpoints.
|
||||
|
||||
To consume the custom endpoints in your Medusa backend, you must transpile them with the `build` command before starting your backend.
|
||||
|
||||
---
|
||||
:::
|
||||
|
||||
## Basic Implementation
|
||||
|
||||
@@ -61,7 +58,7 @@ npm run build
|
||||
|
||||
## Defining Multiple Routes or Middlewares
|
||||
|
||||
Instead of returning an Express router in the function, you can return an array of routes and [middlewares](./add-middleware.mdx).
|
||||
Instead of returning an Express router in the function, you can return an array of routes and [middlewares](./add-middleware-express-route.mdx).
|
||||
|
||||
For example:
|
||||
|
||||
@@ -419,7 +416,7 @@ export default (rootDirectory) => {
|
||||
|
||||
As Medusa uses v4 of Express, you need to manually handle errors thrown asynchronously as explained in [Express's documentation](https://expressjs.com/en/guide/error-handling.html).
|
||||
|
||||
You can use [middlewares](./add-middleware.mdx) to handle errors. You can also use middlewares defined by Medusa, which ensure that your error handling is consistent across your Medusa backend.
|
||||
You can use [middlewares](./add-middleware-express-route.mdx) to handle errors. You can also use middlewares defined by Medusa, which ensure that your error handling is consistent across your Medusa backend.
|
||||
|
||||
:::note
|
||||
|
||||
@@ -0,0 +1,744 @@
|
||||
---
|
||||
addHowToData: true
|
||||
---
|
||||
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
|
||||
# How to Create an API Route
|
||||
|
||||
In this document, you’ll learn how to create API Routes in Medusa.
|
||||
|
||||
:::tip
|
||||
|
||||
v1.17.2 of `@medusajs/medusa` introduced API Routes to replace Express endpoints. You can still use the [Express endpoints approach](./create-express-route.mdx), however, it's highly recommended that you start using API Routes.
|
||||
|
||||
:::
|
||||
|
||||
## Basic Implementation
|
||||
|
||||
```ts title=src/api/store/custom/route.ts
|
||||
import type {
|
||||
MedusaRequest,
|
||||
MedusaResponse,
|
||||
} from "@medusajs/medusa"
|
||||
|
||||
export const GET = (
|
||||
req: MedusaRequest,
|
||||
res: MedusaResponse
|
||||
) => {
|
||||
res.json({
|
||||
message: "[GET] Hello world!",
|
||||
})
|
||||
}
|
||||
|
||||
export const POST = (
|
||||
req: MedusaRequest,
|
||||
res: MedusaResponse
|
||||
) => {
|
||||
res.json({
|
||||
message: "[POST] Hello world!",
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
### API Route Path
|
||||
|
||||
Custom API Routes must be created in a file named `route.ts` or `route.js` under the `src/api` directory of your Medusa backend or plugin. The API Route's path will be the same as the path of its corresponding `route.ts` file relative to `src/api`.
|
||||
|
||||
For example, if you're creating the API route `store/custom`, you must create the file `src/api/store/custom/route.ts`.
|
||||
|
||||
### API Route Method
|
||||
|
||||
`route.ts` can export at least one of the following method handler functions: `GET`, `POST`, `DELETE`, `PUT`, `PATCH`, `OPTIONS`, and `HEAD`. Defining these method handlers adds a new API Route for the corresponding HTTP method at the same path.
|
||||
|
||||
Each of these method handler functions receives two parameters: the `MedusaRequest` which extends Express's [Request](https://expressjs.com/en/api.html#req), and the `MedusaResponse` which extends [Response](https://expressjs.com/en/api.html#res). Both are imported from `@medusajs/medusa`.
|
||||
|
||||
In the example above, `GET` and `POST` API Routes will be added at the `store/custom` path.
|
||||
|
||||
---
|
||||
|
||||
## Building Files
|
||||
|
||||
Custom API Routes must be transpiled and moved to the `dist` directory before you can start consuming them. When you run your backend using either the `medusa develop` or `npx medusa develop` commands, it watches the files under `src` for any changes, then triggers the `build` command and restarts the server.
|
||||
|
||||
However, the build isn't triggered when the backend first starts running, and it's never triggered when the `medusa start` or `npx medusa start` commands are used.
|
||||
|
||||
So, make sure to run the `build` command before starting the backend and testing out your API Routes:
|
||||
|
||||
```bash npm2yarn
|
||||
npm run build
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Medusa API Routes Path Convention
|
||||
|
||||
Although your API Route can be under any path you wish, the Medusa backend uses the following conventions:
|
||||
|
||||
- All storefront REST APIs are prefixed by `/store`. For example, the `/store/products` API Route lets you retrieve the products to display them on your storefront.
|
||||
- All admin REST APIs are prefixed by `/admin`. For example, the `/admin/products` API Route lets you retrieve the products to display them on your admin.
|
||||
|
||||
---
|
||||
|
||||
## Path Parameters
|
||||
|
||||
If your API Route accepts a path parameter, you can place its route file inside a directory with the name `[<PARAMETER_NAME>]`, where `<PARAMETER_NAME>` is the name of your parameter.
|
||||
|
||||
For example, to add an API Route at the path `store/custom/[id]`, create the route file at `src/api/store/custom/[id]/route.ts`.
|
||||
|
||||
You can access a path parameter's value in method handlers using the `MedusaRequest` object's `params` property, which is an object. Each of the `params` keys is a path parameter's name, and its value is the supplied value when sending the request to the API route.
|
||||
|
||||
For example:
|
||||
|
||||
```ts title=src/api/store/custom/[id]/route.ts
|
||||
import type {
|
||||
MedusaRequest,
|
||||
MedusaResponse,
|
||||
} from "@medusajs/medusa"
|
||||
|
||||
export function GET(
|
||||
req: MedusaRequest,
|
||||
res: MedusaResponse
|
||||
) {
|
||||
const id = req.params.id
|
||||
|
||||
// do something with the ID.
|
||||
}
|
||||
```
|
||||
|
||||
An API Route can have more than one path parameter, but each path parameter's nam is unique. If the same path parameter name is used more than once in the same route path, it results in an error and the Medusa Backend won't register the API Route.
|
||||
|
||||
For example, if your API route accepts an author ID and a post ID, the path to your route file can be `src/api/author/[id]/posts/[post_id]/route.ts`. You can then use the `MedusaRequest` object's `params.id` and `params.post_id` to access the values of the path parameters.
|
||||
|
||||
---
|
||||
|
||||
## CORS Configuration
|
||||
|
||||
CORS configurations are automatically added to custom API Routes defined under the `/store` or `/admin` path prefixes based on the [store_cors and admin_cors configurations](../backend/configurations.md#admin_cors-and-store_cors) respectively.
|
||||
|
||||
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:
|
||||
|
||||
```ts title=src/api/middlewares.ts
|
||||
import type {
|
||||
MiddlewaresConfig,
|
||||
} from "@medusajs/medusa"
|
||||
import cors from "cors"
|
||||
|
||||
export const config: MiddlewaresConfig = {
|
||||
routes: [
|
||||
{
|
||||
matcher: "/custom/*",
|
||||
middlewares: [
|
||||
cors({
|
||||
origin: "*",
|
||||
credentials: true,
|
||||
}),
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Parse Request Body Parameters
|
||||
|
||||
By default, the Medusa backend parses the body of all requests sent to your API Routes with the `Content-Type` header set to `application/json` to a JavaScript object. Then, the parsed data is attached to the `MedusaRequest` object's `body` property, which is an object.
|
||||
|
||||
Each of the `body`'s keys are a name of the request body parameters, and its value is the passed value in the request body.
|
||||
|
||||
For example:
|
||||
|
||||
```ts title=src/api/store/custom/route.ts
|
||||
import type {
|
||||
MedusaRequest,
|
||||
MedusaResponse,
|
||||
} from "@medusajs/medusa"
|
||||
|
||||
export const POST = (
|
||||
req: MedusaRequest,
|
||||
res: MedusaResponse
|
||||
) => {
|
||||
const name = req.body.name
|
||||
|
||||
// do something with the data...
|
||||
}
|
||||
```
|
||||
|
||||
If you want to parse other content types, such as `application/x-www-form-urlencoded`, you have to add a [middleware](./add-middleware.mdx) to your API routes that parses that body type.
|
||||
|
||||
For example:
|
||||
|
||||
```ts title=src/api/middlewares.ts
|
||||
import type {
|
||||
MiddlewaresConfig,
|
||||
} from "@medusajs/medusa"
|
||||
import {
|
||||
urlencoded,
|
||||
} from "body-parser"
|
||||
|
||||
export const config: MiddlewaresConfig = {
|
||||
routes: [
|
||||
{
|
||||
matcher: "/store/*",
|
||||
middlewares: [
|
||||
urlencoded({ extended: true }),
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
Note that the `urlencoded` middleware imported from the [body-parser package](https://www.npmjs.com/package/body-parser) attaches the parsed data to the `MedusaRequest` object's `body` property as well.
|
||||
|
||||
---
|
||||
|
||||
## Protected API Routes
|
||||
|
||||
Protected API routes are routes that should only be accessible by logged-in customers or users.
|
||||
|
||||
### Protect Store API Routes
|
||||
|
||||
By default, API routes prefixed by `/store` don't require customer authentication to access the API route. However, you can still access the logged-in customer's ID in the API Route method handler using the `MedusaRequest` object's `user.customer_id`, which will be `undefined` if the customer isn't logged in.
|
||||
|
||||
For example:
|
||||
|
||||
```ts title=src/api/store/custom/route.ts
|
||||
import { CustomerService } from "@medusajs/medusa"
|
||||
import type {
|
||||
MedusaRequest,
|
||||
MedusaResponse,
|
||||
} from "@medusajs/medusa"
|
||||
|
||||
export const GET = async (
|
||||
req: MedusaRequest,
|
||||
res: MedusaResponse
|
||||
) => {
|
||||
const id = req.user.customer_id
|
||||
|
||||
if (!id) {
|
||||
// TODO handle not logged in
|
||||
// customers based on the custom
|
||||
// API route's functionality
|
||||
}
|
||||
|
||||
const customerService = req.scope.resolve<CustomerService>(
|
||||
"customerService"
|
||||
)
|
||||
|
||||
const customer = await customerService.retrieve(id)
|
||||
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
API Routes prefixed by `/store/me`, on the other hand, require customer authentication to access the API Route. You can access the logged-in customer's ID in the API Route method handler using the `MedusaRequest` object's `user.customer_id`.
|
||||
|
||||
If you want to disable authentication requirement on your custom API Route prefixed with `/store/me`, export an `AUTHENTICATE` variable in the route file with its value set to `false`. For example:
|
||||
|
||||
```ts title=src/api/store/me/custom/route.ts
|
||||
import type {
|
||||
MedusaRequest,
|
||||
MedusaResponse,
|
||||
} from "@medusajs/medusa"
|
||||
|
||||
export const GET = async (
|
||||
req: MedusaRequest,
|
||||
res: MedusaResponse
|
||||
) => {
|
||||
res.json({
|
||||
message: "Hello",
|
||||
})
|
||||
}
|
||||
|
||||
export const AUTHENTICATE = false
|
||||
```
|
||||
|
||||
:::note
|
||||
|
||||
This disables authentication requirement on all API Route methods defined in the same file.
|
||||
|
||||
:::
|
||||
|
||||
### Protect Admin API Routes
|
||||
|
||||
By default, all API Routes prefixed by `/admin` require admin user authentication to access the API Route. You can access the logged-in user's ID in the API Route method handler using the `MedusaRequest` object's `user.userId`.
|
||||
|
||||
For example:
|
||||
|
||||
```ts title=src/api/admin/custom/route.ts
|
||||
import type {
|
||||
MedusaRequest,
|
||||
MedusaResponse,
|
||||
UserService,
|
||||
} from "@medusajs/medusa"
|
||||
|
||||
export const GET = async (
|
||||
req: MedusaRequest,
|
||||
res: MedusaResponse
|
||||
) => {
|
||||
const id = req.user.userId
|
||||
|
||||
const userService = req.scope.resolve<UserService>(
|
||||
"userService"
|
||||
)
|
||||
|
||||
const user = await userService.retrieve(id)
|
||||
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
To disable authentication requirement on an admin API Route, export an `AUTHENTICATE` variable in your route file with its value set to `false`.
|
||||
|
||||
For example:
|
||||
|
||||
```ts title=src/api/admin/custom/route.ts
|
||||
import type {
|
||||
MedusaRequest,
|
||||
MedusaResponse,
|
||||
} from "@medusajs/medusa"
|
||||
|
||||
export const GET = async (
|
||||
req: MedusaRequest,
|
||||
res: MedusaResponse
|
||||
) => {
|
||||
res.json({
|
||||
message: "Hello",
|
||||
})
|
||||
}
|
||||
|
||||
export const AUTHENTICATE = false
|
||||
```
|
||||
|
||||
:::note
|
||||
|
||||
This disables authentication requirement on all API Route methods defined in the same file.
|
||||
|
||||
:::
|
||||
|
||||
### Protect Other API Routes
|
||||
|
||||
To protect API routes that aren't prefixed with `/store` or `/admin`, you can use one of the following middlewares exported by `@medusajs/medusa` for authenticating customers or users:
|
||||
|
||||
- `authenticate`: this middleware ensures that only authenticated admin users can access an API Route. You can access the user's ID in the API Route method handler using the `MedusaRequest` object's `user.userId`.
|
||||
- `authenticateCustomer`: this middleware doesn't require a customer to be authenticated, but if a customer is logged in, it attaches their ID to the `MedusaRequest` object's `user.customer_id`.
|
||||
- `requireCustomerAuthentication`: this middleware ensures that only authenticated customers can access an API Route. You can access the customer's ID in the API Route method handler using the `MedusaRequest` object's `user.customer_id`.
|
||||
|
||||
For example:
|
||||
|
||||
```ts title=src/api/middlewares.ts
|
||||
import {
|
||||
authenticate,
|
||||
requireCustomerAuthentication,
|
||||
type MiddlewaresConfig,
|
||||
} from "@medusajs/medusa"
|
||||
|
||||
export const config: MiddlewaresConfig = {
|
||||
routes: [
|
||||
{
|
||||
matcher: "/custom/admin*",
|
||||
middlewares: [authenticate()],
|
||||
},
|
||||
{
|
||||
matcher: "/custom/customer*",
|
||||
middlewares: [requireCustomerAuthentication()],
|
||||
},
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Retrieve Medusa Config
|
||||
|
||||
You can access the configurations exported in `medusa-config.js`, including your custom configurations, by resolving the `configModule` resource using [dependency injection](../fundamentals/dependency-injection.md).
|
||||
|
||||
For example:
|
||||
|
||||
```ts title=src/api/store/custom/route.ts
|
||||
import type {
|
||||
MedusaRequest,
|
||||
MedusaResponse,
|
||||
} from "@medusajs/medusa"
|
||||
import { ConfigModule } from "@medusajs/medusa"
|
||||
|
||||
// This is only helpful if you're
|
||||
// accessing custom configurations
|
||||
// otherwise it's fine to just use `ConfigModule`
|
||||
type MyConfigModule = ConfigModule & {
|
||||
projectConfig: {
|
||||
custom_config?: string
|
||||
}
|
||||
}
|
||||
|
||||
export const GET = (
|
||||
req: MedusaRequest,
|
||||
res: MedusaResponse
|
||||
) => {
|
||||
const configModule = req.scope.resolve<MyConfigModule>(
|
||||
"configModule"
|
||||
)
|
||||
res.json({
|
||||
message: configModule.projectConfig.custom_config,
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Handle Errors
|
||||
|
||||
Medusa provides an `errorHandler` middleware that you can use on your custom API Routes so that your error handling is consistent with the Medusa backend. You can also create custom [middlewares](./add-middleware.mdx) to handle errors.
|
||||
|
||||
To handle errors using Medusa's middlewares, first, import the `errorHandler` middleware from `@medusajs/medusa` and apply it on your routes.
|
||||
|
||||
For example:
|
||||
|
||||
```ts title=src/api/middlewares.ts
|
||||
import {
|
||||
errorHandler,
|
||||
type MiddlewaresConfig,
|
||||
} from "@medusajs/medusa"
|
||||
|
||||
export const config: MiddlewaresConfig = {
|
||||
routes: [
|
||||
{
|
||||
matcher: "/store/custom*",
|
||||
middlewares: [errorHandler()],
|
||||
},
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
Make sure it's applied after all other middlewares.
|
||||
|
||||
Then, wrap the method handler function of every API route method with the `wrapHandler` function imported from `@medusajs/medusa`. For example:
|
||||
|
||||
```ts title=src/api/store/custom/route.ts
|
||||
import {
|
||||
MedusaRequest,
|
||||
MedusaResponse,
|
||||
wrapHandler,
|
||||
} from "@medusajs/medusa"
|
||||
|
||||
export const GET = wrapHandler(async (
|
||||
req: MedusaRequest,
|
||||
res: MedusaResponse
|
||||
) => {
|
||||
res.json({
|
||||
message: "[GET] Hello world!",
|
||||
})
|
||||
})
|
||||
|
||||
export const POST = wrapHandler(async (
|
||||
req: MedusaRequest,
|
||||
res: MedusaResponse
|
||||
) => {
|
||||
res.json({
|
||||
message: "[POST] Hello world!",
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
Now all errors thrown in your custom API Routes, or in resources you use within your API Route such as services, are caught and returned to the user.
|
||||
|
||||
### Using MedusaError
|
||||
|
||||
If you throw errors like this:
|
||||
|
||||
```ts
|
||||
throw new Error ("Post was not found")
|
||||
```
|
||||
|
||||
The API Route returns the following object error in the response:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": "unknown_error",
|
||||
"type": "unknown_error",
|
||||
"message": "An unknown error occurred."
|
||||
}
|
||||
```
|
||||
|
||||
To ensure your error message is relayed in the response, it's recommended to use `MedusaError` imported from `@medusajs/utils` as the thrown error instead.
|
||||
|
||||
For example:
|
||||
|
||||
```ts
|
||||
import { MedusaError } from "@medusajs/utils"
|
||||
|
||||
// ...
|
||||
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.NOT_FOUND,
|
||||
"Post was not found"
|
||||
)
|
||||
```
|
||||
|
||||
The constructor of `MedusaError` accepts the following parameters:
|
||||
|
||||
1. The first parameter is the error's type. You can use one of the predefined errors under `MedusaError.Types`, such as `MedusaError.Types.NOT_FOUND` which sets the response status code to `404` automatically.
|
||||
2. The second parameter is the message of the error.
|
||||
3. The third parameter is an optional code, which is a string, that's returned in the error object.
|
||||
|
||||
After using `MedusaError`, the returned error in the response provides a clearer message:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "not_found",
|
||||
"message": "Post was not found"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Use Other Resources
|
||||
|
||||
Resources, such as services, that are registered in the [dependency container](../fundamentals/dependency-injection.md) can be retrieved in an API Route's handler method using the `MedusaRequest` object's `scope.resolve` method.
|
||||
|
||||
The `scope` method accepts as a parameter the resource's registration name in the [dependency container](../fundamentals/dependency-injection.md).
|
||||
|
||||
### Example: Retrieve Repository
|
||||
|
||||
:::tip
|
||||
|
||||
Posts are represented by a custom entity not covered in this guide. You can refer to the [entities](../entities/create.mdx#adding-relations) for more details on how to create a custom entity.
|
||||
|
||||
:::
|
||||
|
||||
```ts title=src/api/store/posts/route.ts
|
||||
import type {
|
||||
MedusaRequest,
|
||||
MedusaResponse,
|
||||
} from "@medusajs/medusa"
|
||||
import {
|
||||
PostRepository,
|
||||
} from "../../../repositories/post"
|
||||
import { EntityManager } from "typeorm"
|
||||
|
||||
export const GET = async (
|
||||
req: MedusaRequest,
|
||||
res: MedusaResponse
|
||||
) => {
|
||||
const postRepository =
|
||||
req.scope.resolve<PostRepository>("postRepository")
|
||||
const manager = req.scope.resolve<EntityManager>("manager")
|
||||
const postRepo = manager.withRepository(postRepository)
|
||||
|
||||
res.json({
|
||||
posts: await postRepo.find(),
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
Notice that to retrieve an instance of the repository, you need to retrieve first Typeorm's Entity Manager from the dependency container, then use its `withRepository` method.
|
||||
|
||||
### Example: Retrieve Service
|
||||
|
||||
:::note
|
||||
|
||||
`PostService` is a custom service that is not covered in this guide. You can refer to the [services](../services/create-service.mdx) documentation for more details on how to create a custom service, and find an [example of PostService](../services/create-service.mdx#example-services-with-crud-operations)
|
||||
|
||||
:::
|
||||
|
||||
```ts title=src/api/store/posts/route.ts
|
||||
import type {
|
||||
MedusaRequest,
|
||||
MedusaResponse,
|
||||
} from "@medusajs/medusa"
|
||||
import { PostService } from "../../../services/post"
|
||||
|
||||
export const GET = async (
|
||||
req: MedusaRequest,
|
||||
res: MedusaResponse
|
||||
) => {
|
||||
const postService: PostService = req.scope.resolve(
|
||||
"postService"
|
||||
)
|
||||
|
||||
res.json({
|
||||
posts: await postService.list(),
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Ignored Files and Directories
|
||||
|
||||
Files and directories prefixed with `_` are ignored. This can be helpful if you want to implement API Route method handlers in different files, then reference them in your `route.ts` file.
|
||||
|
||||
For example:
|
||||
|
||||
<Tabs groupId="files" isCodeTabs={true}>
|
||||
<TabItem value="custom-route" label="src/api/custom/route.ts" default>
|
||||
|
||||
```ts
|
||||
import getProducts from "../_methods/get-products"
|
||||
|
||||
export const GET = getProducts
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="internal-method" label="src/api/_methods/get-product.ts">
|
||||
|
||||
```ts
|
||||
import {
|
||||
MedusaRequest,
|
||||
MedusaResponse,
|
||||
ProductService,
|
||||
} from "@medusajs/medusa"
|
||||
|
||||
export default async function (
|
||||
req: MedusaRequest,
|
||||
res: MedusaResponse
|
||||
) {
|
||||
const productService = req.scope.resolve<ProductService>(
|
||||
"productService"
|
||||
)
|
||||
|
||||
const products = await productService.list({})
|
||||
|
||||
res.json({
|
||||
products,
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
---
|
||||
|
||||
## Example: CRUD API Routes
|
||||
|
||||
This section provides an example of creating API Routes that perform Create, Read, Update, and Delete (CRUD) operations.
|
||||
|
||||
:::note
|
||||
|
||||
You can refer to the [Entities](../entities/create.mdx#adding-relations) and [Services](../services/create-service.mdx#example-services-with-crud-operations) documentation to learn how to create the custom entities and services used in this example.
|
||||
|
||||
:::
|
||||
|
||||
<Tabs groupId="files" isCodeTabs={true}>
|
||||
<TabItem value="posts-routes" label="src/api/admin/posts/route.ts" default>
|
||||
|
||||
```ts
|
||||
import type {
|
||||
MedusaRequest,
|
||||
MedusaResponse,
|
||||
} from "@medusajs/medusa"
|
||||
import { PostService } from "../../../services/post"
|
||||
|
||||
// list posts
|
||||
export const GET = async (
|
||||
req: MedusaRequest,
|
||||
res: MedusaResponse
|
||||
) => {
|
||||
const postService: PostService = req.scope.resolve(
|
||||
"postService"
|
||||
)
|
||||
|
||||
res.json({
|
||||
posts: await postService.list(),
|
||||
})
|
||||
}
|
||||
|
||||
// create a post
|
||||
export const POST = async (
|
||||
req: MedusaRequest,
|
||||
res: MedusaResponse
|
||||
) => {
|
||||
const postService: PostService = req.scope.resolve(
|
||||
"postService"
|
||||
)
|
||||
|
||||
// basic validation of request body
|
||||
if (!req.body.title || !req.body.author_id) {
|
||||
throw new Error("`title` and `author_id` are required.")
|
||||
}
|
||||
|
||||
const post = await postService.create(req.body)
|
||||
|
||||
res.json({
|
||||
post,
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="posts-id-routes" label="src/api/admin/posts/[id]/route.ts">
|
||||
|
||||
```ts
|
||||
import type {
|
||||
MedusaRequest,
|
||||
MedusaResponse,
|
||||
} from "@medusajs/medusa"
|
||||
import { PostService } from "../../../services/post"
|
||||
|
||||
// retrieve a post by its ID
|
||||
export const GET = async (
|
||||
req: MedusaRequest,
|
||||
res: MedusaResponse
|
||||
) => {
|
||||
const postService: PostService = req.scope.resolve(
|
||||
"postService"
|
||||
)
|
||||
|
||||
const post = await postService.retrieve(req.params.id)
|
||||
|
||||
res.json({
|
||||
post,
|
||||
})
|
||||
}
|
||||
|
||||
// update a post by its ID
|
||||
export const POST = async (
|
||||
req: MedusaRequest,
|
||||
res: MedusaResponse
|
||||
) => {
|
||||
const postService: PostService = req.scope.resolve(
|
||||
"postService"
|
||||
)
|
||||
|
||||
// basic validation of request body
|
||||
if (req.body.id) {
|
||||
throw new Error("Can't update post ID")
|
||||
}
|
||||
|
||||
const post = await postService.update(
|
||||
req.params.id,
|
||||
req.body
|
||||
)
|
||||
|
||||
res.json({
|
||||
post,
|
||||
})
|
||||
}
|
||||
|
||||
// delete a post by its ID
|
||||
export const DELETE = async (
|
||||
req: MedusaRequest,
|
||||
res: MedusaResponse
|
||||
) => {
|
||||
const postService: PostService = req.scope.resolve(
|
||||
"postService"
|
||||
)
|
||||
|
||||
await postService.delete(req.params.id)
|
||||
|
||||
res.status(200).end()
|
||||
}
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
---
|
||||
|
||||
## See Also
|
||||
|
||||
- [Storefront API Reference](https://docs.medusajs.com/api/store)
|
||||
- [Admin API Reference](https://docs.medusajs.com/api/admin)
|
||||
@@ -0,0 +1,171 @@
|
||||
---
|
||||
addHowToData: true
|
||||
---
|
||||
|
||||
import Troubleshooting from '@site/src/components/Troubleshooting'
|
||||
import ServiceLifetimeSection from '../../troubleshooting/awilix-resolution-error/_service-lifetime.md'
|
||||
import CustomRegistrationSection from '../../troubleshooting/awilix-resolution-error/_custom-registration.md'
|
||||
|
||||
# Example: Access Logged-In User
|
||||
|
||||
This document gives an example of how you can use middlewares to register the logged-in user in the dependency container of your commerce application. You can then access the logged-in user in other resources, such as services.
|
||||
|
||||
:::tip
|
||||
|
||||
You can apply the same steps if you want to register the current customer.
|
||||
|
||||
:::
|
||||
|
||||
Learn more about [middlewares in its guide](./add-middleware.mdx).
|
||||
|
||||
## Step 1: Create the Middleware
|
||||
|
||||
Create the file `src/api/middlewares.ts` with the following content:
|
||||
|
||||
```ts title=src/api/middlewares.ts
|
||||
import type {
|
||||
MiddlewaresConfig,
|
||||
User,
|
||||
UserService,
|
||||
} from "@medusajs/medusa"
|
||||
import type {
|
||||
MedusaNextFunction,
|
||||
MedusaRequest,
|
||||
MedusaResponse,
|
||||
} from "@medusajs/medusa"
|
||||
|
||||
const registerLoggedInUser = async (
|
||||
req: MedusaRequest,
|
||||
res: MedusaResponse,
|
||||
next: MedusaNextFunction
|
||||
) => {
|
||||
let loggedInUser: User | null = null
|
||||
|
||||
if (req.user && req.user.userId) {
|
||||
const userService =
|
||||
req.scope.resolve("userService") as UserService
|
||||
loggedInUser = await userService.retrieve(req.user.userId)
|
||||
}
|
||||
|
||||
req.scope.register({
|
||||
loggedInUser: {
|
||||
resolve: () => loggedInUser,
|
||||
},
|
||||
})
|
||||
|
||||
next()
|
||||
}
|
||||
|
||||
export const config: MiddlewaresConfig = {
|
||||
routes: [
|
||||
{
|
||||
matcher: "/admin/products",
|
||||
middlewares: [registerLoggedInUser],
|
||||
},
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
This creates a `registerLoggedInUser` middleware that handles registering the logged-in user in the dependency container.
|
||||
|
||||
It then applies this middleware on the `/admin/products` API Route. You can change that to be the route of any other API Route or a pattern that matches multiple routes.
|
||||
|
||||
---
|
||||
|
||||
## Step 2: Use in a Service
|
||||
|
||||
Access the logged-in user resource in the constructor of a service:
|
||||
|
||||
<!-- eslint-disable prefer-rest-params -->
|
||||
|
||||
```ts
|
||||
import { Lifetime } from "awilix"
|
||||
import {
|
||||
TransactionBaseService,
|
||||
User,
|
||||
} from "@medusajs/medusa"
|
||||
|
||||
class HelloService extends TransactionBaseService {
|
||||
|
||||
protected readonly loggedInUser_: User | null
|
||||
|
||||
constructor(container, options) {
|
||||
super(...arguments)
|
||||
|
||||
try {
|
||||
this.loggedInUser_ = container.loggedInUser
|
||||
} catch (e) {
|
||||
// avoid errors when backend first runs
|
||||
}
|
||||
}
|
||||
|
||||
// ...
|
||||
}
|
||||
|
||||
export default HelloService
|
||||
```
|
||||
|
||||
If you're accessing it in an extended core service, it’s important to change the lifetime of the service to `Lifetime.SCOPED`. For example:
|
||||
|
||||
<!-- eslint-disable prefer-rest-params -->
|
||||
|
||||
```ts
|
||||
import { Lifetime } from "awilix"
|
||||
import {
|
||||
ProductService as MedusaProductService,
|
||||
User,
|
||||
} from "@medusajs/medusa"
|
||||
|
||||
// extend core product service
|
||||
class ProductService extends MedusaProductService {
|
||||
// The default life time for a core service is SINGLETON
|
||||
static LIFE_TIME = Lifetime.SCOPED
|
||||
|
||||
protected readonly loggedInUser_: User | null
|
||||
|
||||
constructor(container, options) {
|
||||
super(...arguments)
|
||||
|
||||
this.loggedInUser_ = container.loggedInUser
|
||||
}
|
||||
}
|
||||
|
||||
export default ProductService
|
||||
```
|
||||
|
||||
You can learn more about the importance of changing the service lifetime in the [Middlewares documentation](./add-middleware.mdx#note-about-services-lifetime).
|
||||
|
||||
---
|
||||
|
||||
## Step 3: Test it Out
|
||||
|
||||
To test out your implementation, run the following command in the root directory of the Medusa backend to transpile your changes:
|
||||
|
||||
```bash npm2yarn
|
||||
npm run build
|
||||
```
|
||||
|
||||
Then, run your backend with the following command:
|
||||
|
||||
```bash npm2yarn
|
||||
npx medusa develop
|
||||
```
|
||||
|
||||
If you try accessing the API Routes you added the middleware to, you should see your implementation working as expected.
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
<Troubleshooting
|
||||
sections={[
|
||||
{
|
||||
title: 'AwilixResolutionError: Could Not Resolve X',
|
||||
content: <ServiceLifetimeSection />
|
||||
},
|
||||
{
|
||||
title: 'AwilixResolutionError: Could Not Resolve X (Custom Registration)',
|
||||
content: <CustomRegistrationSection />
|
||||
}
|
||||
]}
|
||||
/>
|
||||
+9
-10
@@ -1,21 +1,20 @@
|
||||
---
|
||||
description: 'Learn how to extend a validator. This is useful when you want to pass additional data to endpoints in the Medusa core.'
|
||||
addHowToData: true
|
||||
---
|
||||
|
||||
# How to Extend an Endpoint Validator
|
||||
# How to Extend an API Route Validator
|
||||
|
||||
In this guide, you'll learn how to extend an endpoint validator from the Medusa core.
|
||||
In this guide, you'll learn how to extend an API Route validator from the Medusa core.
|
||||
|
||||
## Overview
|
||||
|
||||
Request fields passed to endpoints that are defined in the Medusa core are validated to ensure that only expected fields are passed, and the passed fields are of correct types.
|
||||
Request fields passed to API Routes defined in the Medusa core are validated to ensure that only expected fields are passed, and the passed fields are of correct types.
|
||||
|
||||
In some scenarios, you may need to allow passing custom fields into an existing endpoint. If a custom field is passed to an endpoint in the core, the endpoint returns an error in the response.
|
||||
In some scenarios, you may need to allow passing custom fields into an existing API Route. If a custom field is passed to an API Route in the core, the API Route returns an error in the response.
|
||||
|
||||
To allow passing custom fields into core endpoints, you must extend Validators. Validators are classes that are used by the core to validate the request parameters to an endpoint.
|
||||
To allow passing custom fields into core API Routes, you must extend Validators. Validators are classes that are used by the core to validate the request parameters to an API Route.
|
||||
|
||||
This guide explains how to extend a validator to allow passing custom fields to an endpoint. You'll be extending the validator of the admin API Create Product endpoint as an example.
|
||||
This guide explains how to extend a validator to allow passing custom fields to an API Route. You'll be extending the validator of the admin API Create Product route as an example.
|
||||
|
||||
---
|
||||
|
||||
@@ -59,13 +58,13 @@ registerOverriddenValidators(AdminPostProductsReq)
|
||||
In this code snippet you:
|
||||
|
||||
1. Import the `registerOverriddenValidators` function from the `@medusajs/medusa` package. This utility function allows you to extend validators in the core.
|
||||
2. Import the `AdminPostProductsReq` class from `@medusajs/medusa` as `MedusaAdminPostProductsReq` since this guide extends the Create Product endpoint validator. If you're extending a different validator, make sure to import it instead.
|
||||
2. Import the `AdminPostProductsReq` class from `@medusajs/medusa` as `MedusaAdminPostProductsReq` since this guide extends the Create Product API Route validator. If you're extending a different validator, make sure to import it instead.
|
||||
3. Create a class `AdminPostProductsReq` that extends `MedusaAdminPostProductsReq` and adds a new field `custom_field`. Notice that the name of the class must be the same name of the validator defined in the core. `custom_field` has the type `string`. You can change the type or name of the field, or add more fields.
|
||||
4. Call `registerOverriddenValidators` passing it the `AdminPostProductsReq` class you created. This will override the validator defined in the core to include the new field `custom_field` among the existing fields defined in the core.
|
||||
|
||||
:::tip
|
||||
|
||||
Validators are defined in the same file as the endpoint. To find the validator you need to override, find the endpoint file under `@medusajs/medusa/dist/api/routes` and import the validator in that file.
|
||||
Validators are defined in the same file as the API Route. To find the validator you need to override, find the API Route file under `@medusajs/medusa/dist/api/routes` and import the validator in that file.
|
||||
|
||||
:::
|
||||
|
||||
@@ -80,4 +79,4 @@ npm run build
|
||||
npx medusa develop
|
||||
```
|
||||
|
||||
Then, send a request to the endpoint you extended passing it your custom fields. To test out the example in this guide, send an [authenticated request](https://docs.medusajs.com/api/admin#authentication) to the [Create Product endpoint](https://docs.medusajs.com/api/admin#products_postproducts) and pass it the `custom_field` body parameter. The request should execute with no errors.
|
||||
Then, send a request to the API Route you extended passing it your custom fields. To test out the example in this guide, send an [authenticated request](https://docs.medusajs.com/api/admin#authentication) to the [Create Product API Route](https://docs.medusajs.com/api/admin#products_postproducts) and pass it the `custom_field` body parameter. The request should execute with no errors.
|
||||
@@ -0,0 +1,68 @@
|
||||
import DocCardList from '@theme/DocCardList';
|
||||
import Icons from '@theme/Icon';
|
||||
|
||||
# API Routes
|
||||
|
||||
In this document, you’ll learn what API Routes are in Medusa.
|
||||
|
||||
## Introduction
|
||||
|
||||
The Medusa Backend is a Node.js headless server. It exposes commerce functionalities implemented by Medusa's [commerce module](../../modules/overview.mdx) through REST APIs that external or frontend applications can access to process or retrieve data.
|
||||
|
||||
The backend exposes two types of API routes: Store APIs and Admin APIs. The Store APIs are typically accessed from the storefront. For example, you can use the Store APIs to show customers available products or implement a cart and checkout flow.
|
||||
|
||||
The Admin APIs are typically accessed from an admin dashboard. For example, you can use the Admin APIs to allow admins to manage the store’s data such as products, orders, and so on.
|
||||
|
||||
<DocCardList colSize={6} items={[
|
||||
{
|
||||
type: 'link',
|
||||
href: 'https://docs.medusajs.com/api/store',
|
||||
label: 'Store APIs',
|
||||
customProps: {
|
||||
icon: Icons['server-solid'],
|
||||
description: 'Check out available Store REST APIs.'
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'link',
|
||||
href: 'https://docs.medusajs.com/api/admin',
|
||||
label: 'Admin APIs',
|
||||
customProps: {
|
||||
icon: Icons['server-solid'],
|
||||
description: 'Check out available Admin REST APIs.'
|
||||
}
|
||||
},
|
||||
]} />
|
||||
|
||||
---
|
||||
|
||||
## Custom Development
|
||||
|
||||
Aside from using the Medusa backend's API routes, developers can create their own API routes either directly in the Medusa Backend or in a plugin.
|
||||
|
||||
:::tip
|
||||
|
||||
As the core Medusa package is completely customizable, developers can also extend the functionality even further to implement GraphQL routes.
|
||||
|
||||
:::
|
||||
|
||||
<DocCardList colSize={6} items={[
|
||||
{
|
||||
type: 'link',
|
||||
href: '/development/api-routes/create',
|
||||
label: 'Create an API Route',
|
||||
customProps: {
|
||||
icon: Icons['academic-cap-solid'],
|
||||
description: 'Learn how to create an API Route in Medusa.'
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'link',
|
||||
href: '/development/api-routes/add-middleware',
|
||||
label: 'Add a Middleware',
|
||||
customProps: {
|
||||
icon: Icons['academic-cap-solid'],
|
||||
description: 'Learn how to add a middleware in Medusa.'
|
||||
}
|
||||
},
|
||||
]} />
|
||||
@@ -52,9 +52,9 @@ This section includes all configurations that belong to the `projectConfig` prop
|
||||
|
||||
### admin_cors and store_cors
|
||||
|
||||
The Medusa backend’s endpoints 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 endpoints.
|
||||
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 endpoints, and `store_cors` is used to specify the accepted URLs or patterns for store endpoints.
|
||||
`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:
|
||||
|
||||
@@ -129,7 +129,7 @@ Its value is an object that has the following properties:
|
||||
- `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 endpoints, you can pass in the request header `"x-no-compression": true`.
|
||||
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 = {
|
||||
|
||||
@@ -136,9 +136,9 @@ This directory holds all Medusa admin customizations. The main subdirectories of
|
||||
|
||||
### api
|
||||
|
||||
This directory holds all custom endpoints. You can create as many subdirectories and files that hold endpoint definitions, but only endpoints exported by the `index.ts` file are registered in the Medusa backend.
|
||||
This directory holds all custom API Routes, which are defined in `route.ts` or `route.js` files. These files can be created in sub-directories of the `api` directory based on the API Route's path.
|
||||
|
||||
**Read more:** [Endpoints](../endpoints/overview.mdx)
|
||||
**Read more:** [API Routes](../api-routes/overview.mdx)
|
||||
|
||||
### loaders
|
||||
|
||||
|
||||
@@ -288,7 +288,7 @@ If you follow along with the JS Client code snippets, make sure to [install and
|
||||
|
||||
### Create Batch Job
|
||||
|
||||
The first step is to create a batch job using the [Create Batch Job endpoint](https://docs.medusajs.com/api/admin#batch-jobs_postbatchjobs). In the body of the request, you must set the `type` to the value of `batchType` in the batch job strategy you created.
|
||||
The first step is to create a batch job using the [Create Batch Job API Route](https://docs.medusajs.com/api/admin#batch-jobs_postbatchjobs). In the body of the request, you must set the `type` to the value of `batchType` in the batch job strategy you created.
|
||||
|
||||
For example, this creates a batch job of the type `publish-products`:
|
||||
|
||||
|
||||
@@ -103,7 +103,7 @@ npm run build
|
||||
|
||||
Since you didn’t create a new batch job type and overwrote the functionality of the strategy, you can test out your functionality using the [same steps used with the default strategy](./create.mdx#test-your-batch-job-strategy).
|
||||
|
||||
Specifically, since you create batch jobs using the [Create Batch Job](https://docs.medusajs.com/api/admin#batch-jobs_postbatchjobs) endpoint which accepts the batch job type as a body parameter, you just need to send the same type you used for this field. In the example of this documentation, the `type` would be `product-import`.
|
||||
Specifically, since you create batch jobs using the [Create Batch Job API Route](https://docs.medusajs.com/api/admin#batch-jobs_postbatchjobs) which accepts the batch job type as a body parameter, you just need to send the same type you used for this field. In the example of this documentation, the `type` would be `product-import`.
|
||||
|
||||
If you overwrote the import functionality, you can follow [these steps to learn how to import products using the Admin APIs](../../modules/products/admin/import-products.mdx).
|
||||
|
||||
|
||||
@@ -49,15 +49,15 @@ When you create a batch job strategy, the `batchType` class property indicates t
|
||||
|
||||
A batch job’s flow from creation to completion is:
|
||||
|
||||
1. A batch job is created using the [Create Batch Job API endpoint](https://docs.medusajs.com/api/admin#batch-jobs_postbatchjobs).
|
||||
1. A batch job is created using the [Create Batch Job API route](https://docs.medusajs.com/api/admin#batch-jobs_postbatchjobs).
|
||||
2. Once the batch job is created, the batch job’s status is changed to `created` and the `batch.created` event is triggered by the `BatchJobService`.
|
||||
3. The `BatchJobSubscriber` handles the `created` event. It resolves the batch job strategy based on the `type` of the batch job, then uses it to pre-process the batch job. After this, the batch job’s status is changed to `pre_processed`. Only when the batch job has the status `pre_processed` can be confirmed.
|
||||
4. If `dry_run` is not set in the Create Batch Job request in step one or if it is set to `false`, the batch job will automatically be confirmed after processing. Otherwise, if `dry_run` is set to `true`, the batch job can be confirmed using the [Confirm Batch Job API](https://docs.medusajs.com/api/admin#batch-jobs_postbatchjobsbatchjobconfirmprocessing) endpoint.
|
||||
4. If `dry_run` is not set in the Create Batch Job request in step one or if it is set to `false`, the batch job will automatically be confirmed after processing. Otherwise, if `dry_run` is set to `true`, the batch job can be confirmed using the [Confirm Batch Job API Route](https://docs.medusajs.com/api/admin#batch-jobs_postbatchjobsbatchjobconfirmprocessing).
|
||||
5. Once the batch job is confirmed, the batch job’s status is changed to `confirmed` and the `batch.confirmed` event is triggered by the `BatchJobService`.
|
||||
6. The `BatchJobSubscriber` handles the `confirmed` event. It resolves the batch job strategy, then uses it to process the batch job.
|
||||
7. Once the batch job is processed successfully, the batch job has the status `completed`.
|
||||
|
||||
You can track the progress of the batch job at any point using the [Retrieve Batch Job](https://docs.medusajs.com/api/admin#batch-jobs_getbatchjobsbatchjob) endpoint.
|
||||
You can track the progress of the batch job at any point using the [Retrieve Batch Job API Route](https://docs.medusajs.com/api/admin#batch-jobs_getbatchjobsbatchjob).
|
||||
|
||||
:::info
|
||||
|
||||
|
||||
@@ -1,220 +0,0 @@
|
||||
---
|
||||
description: 'In this document, you’ll see an example of how you can use middlewares and endpoints to register the logged-in user in the dependency container of your commerce application.'
|
||||
addHowToData: true
|
||||
---
|
||||
|
||||
import Troubleshooting from '@site/src/components/Troubleshooting'
|
||||
import ServiceLifetimeSection from '../../troubleshooting/awilix-resolution-error/_service-lifetime.md'
|
||||
import CustomRegistrationSection from '../../troubleshooting/awilix-resolution-error/_custom-registration.md'
|
||||
|
||||
# Example: Access Logged-In User
|
||||
|
||||
In this document, you’ll see an example of how you can use middlewares and endpoints to register the logged-in user in the dependency container of your commerce application. You can then access the logged-in user in other resources, such as services.
|
||||
|
||||
This guide showcases how to register the logged-in admin user, but you can apply the same steps if you want to register the current customer.
|
||||
|
||||
This documentation does not explain the basics of [middlewares](./add-middleware.mdx) and [endpoints](./create.mdx). You can refer to their respective guides for more details about each.
|
||||
|
||||
## Step 1: Create the Middleware
|
||||
|
||||
Create the file `src/api/middlewares/logged-in-user.ts` with the following content:
|
||||
|
||||
```ts title=src/api/middlewares/logged-in-user.ts
|
||||
import { User, UserService } from "@medusajs/medusa"
|
||||
|
||||
export async function registerLoggedInUser(req, res, next) {
|
||||
let loggedInUser: User | null = null
|
||||
|
||||
if (req.user && req.user.userId) {
|
||||
const userService =
|
||||
req.scope.resolve("userService") as UserService
|
||||
loggedInUser = await userService.retrieve(req.user.userId)
|
||||
}
|
||||
|
||||
req.scope.register({
|
||||
loggedInUser: {
|
||||
resolve: () => loggedInUser,
|
||||
},
|
||||
})
|
||||
|
||||
next()
|
||||
}
|
||||
```
|
||||
|
||||
This retrieves the ID of the current user to retrieve an instance of it, then registers it in the scope under the name `loggedInUser`.
|
||||
|
||||
---
|
||||
|
||||
## Step 2: Apply Middleware on Endpoint
|
||||
|
||||
If you don't have the `cors` package installed, make sure to install it first:
|
||||
|
||||
```bash npm2yarn
|
||||
npm install cors
|
||||
```
|
||||
|
||||
Then, create the file `src/api/routes/create-product.ts` with the following content:
|
||||
|
||||
```ts title=src/api/routes/create-product.ts
|
||||
import cors from "cors"
|
||||
import { Router } from "express"
|
||||
import {
|
||||
registerLoggedInUser,
|
||||
} from "../middlewares/logged-in-user"
|
||||
import
|
||||
authenticate
|
||||
from "@medusajs/medusa/dist/api/middlewares/authenticate"
|
||||
|
||||
const router = Router()
|
||||
|
||||
export default function (adminCorsOptions) {
|
||||
// This router will be applied before the core routes.
|
||||
// Therefore, the middleware will be executed
|
||||
// before the create product handler is hit
|
||||
router.use(
|
||||
"/admin/products",
|
||||
cors(adminCorsOptions),
|
||||
authenticate(),
|
||||
registerLoggedInUser
|
||||
)
|
||||
return router
|
||||
}
|
||||
```
|
||||
|
||||
In the example above, the middleware is applied on the `/admin/products` core endpoint. However, you can apply it on any other endpoint. You can also apply it to custom endpoints.
|
||||
|
||||
For endpoints that require Cross-Origin Resource Origin (CORS) options, such as core endpoints, you must pass the CORS options to the middleware as well since it will be executed before the underlying endpoint.
|
||||
|
||||
:::tip
|
||||
|
||||
In the above code snippet, the `authenticate` middleware imported from `@medusajs/medusa` is used to ensure that the user is logged in first. If you're implementing this for middleware to register the logged-in customer, make sure to use the [customer's authenticate middleware](./create.mdx#protect-store-routes).
|
||||
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## Step 3: Register Endpoint in the API
|
||||
|
||||
Create the file `src/api/index.ts` with the following content:
|
||||
|
||||
```ts title=src/api/index.ts
|
||||
import configLoader from "@medusajs/medusa/dist/loaders/config"
|
||||
import createProductRouter from "./routes/create-product"
|
||||
|
||||
export default function (rootDirectory: string) {
|
||||
const config = configLoader(rootDirectory)
|
||||
|
||||
const adminCors = {
|
||||
origin: config.projectConfig.admin_cors.split(","),
|
||||
credentials: true,
|
||||
}
|
||||
|
||||
const productRouters = [
|
||||
createProductRouter(adminCors),
|
||||
]
|
||||
|
||||
return [...productRouters]
|
||||
}
|
||||
```
|
||||
|
||||
This exports an array of endpoints, one of them being the product endpoint that you applied the middleware on in the second step. You can export more endpoints as well.
|
||||
|
||||
---
|
||||
|
||||
## Step 4: Use in a Service
|
||||
|
||||
You can now access the logged-in user in a service. For example, to access it in a custom service:
|
||||
|
||||
<!-- eslint-disable prefer-rest-params -->
|
||||
|
||||
```ts
|
||||
import { Lifetime } from "awilix"
|
||||
import {
|
||||
TransactionBaseService,
|
||||
User,
|
||||
} from "@medusajs/medusa"
|
||||
|
||||
class HelloService extends TransactionBaseService {
|
||||
|
||||
protected readonly loggedInUser_: User | null
|
||||
|
||||
constructor(container, options) {
|
||||
super(...arguments)
|
||||
|
||||
try {
|
||||
this.loggedInUser_ = container.loggedInUser
|
||||
} catch (e) {
|
||||
// avoid errors when backend first runs
|
||||
}
|
||||
}
|
||||
|
||||
// ...
|
||||
}
|
||||
|
||||
export default HelloService
|
||||
```
|
||||
|
||||
If you're accessing it in an extended core service, it’s important to change the lifetime of the service to `Lifetime.SCOPED`. For example:
|
||||
|
||||
<!-- eslint-disable prefer-rest-params -->
|
||||
|
||||
```ts
|
||||
import { Lifetime } from "awilix"
|
||||
import {
|
||||
ProductService as MedusaProductService,
|
||||
User,
|
||||
} from "@medusajs/medusa"
|
||||
|
||||
// extend core product service
|
||||
class ProductService extends MedusaProductService {
|
||||
// The default life time for a core service is SINGLETON
|
||||
static LIFE_TIME = Lifetime.SCOPED
|
||||
|
||||
protected readonly loggedInUser_: User | null
|
||||
|
||||
constructor(container, options) {
|
||||
super(...arguments)
|
||||
|
||||
this.loggedInUser_ = container.loggedInUser
|
||||
}
|
||||
}
|
||||
|
||||
export default ProductService
|
||||
```
|
||||
|
||||
You can learn more about the importance of changing the service lifetime in the [Middlewares documentation](./add-middleware.mdx#note-about-services-lifetime).
|
||||
|
||||
---
|
||||
|
||||
## Step 5: Test it Out
|
||||
|
||||
To test out your implementation, run the following command in the root directory of the Medusa backend to transpile your changes:
|
||||
|
||||
```bash npm2yarn
|
||||
npm run build
|
||||
```
|
||||
|
||||
Then, run your backend with the following command:
|
||||
|
||||
```bash npm2yarn
|
||||
npx medusa develop
|
||||
```
|
||||
|
||||
If you try accessing the endpoints you added the middleware to, you should see your implementation working as expected.
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
<Troubleshooting
|
||||
sections={[
|
||||
{
|
||||
title: 'AwilixResolutionError: Could Not Resolve X',
|
||||
content: <ServiceLifetimeSection />
|
||||
},
|
||||
{
|
||||
title: 'AwilixResolutionError: Could Not Resolve X (Custom Registration)',
|
||||
content: <CustomRegistrationSection />
|
||||
}
|
||||
]}
|
||||
/>
|
||||
@@ -1,76 +0,0 @@
|
||||
---
|
||||
description: "Learn what endpoints are in Medusa. Endpoints are REST APIs that allow a frontend or external system to interact with the Backend."
|
||||
---
|
||||
|
||||
import DocCardList from '@theme/DocCardList';
|
||||
import Icons from '@theme/Icon';
|
||||
|
||||
# Endpoints
|
||||
|
||||
In this document, you’ll learn what endpoints are in Medusa.
|
||||
|
||||
## Introduction
|
||||
|
||||
The Medusa Backend is a web server built on top of [Express](https://expressjs.com/), a Node.js web framework. This provides developers with all the functionalities available within Express during development. One of those are endpoints.
|
||||
|
||||
Endpoints are REST APIs that allow a frontend or an external system to interact with the Medusa Backend to retrieve and process data, or perform business logic. Endpoints are [Express routes](https://expressjs.com/en/starter/basic-routing.html).
|
||||
|
||||
Each [commerce module](../../modules/overview.mdx) contains a set of endpoints specific to the functionalities that it provides. Since the core package that powers the Medusa Backend acts as an orchestrator of commerce modules and exposes their endpoints, the endpoints of each of these commerce modules are available within the Medusa Backend.
|
||||
|
||||
The commerce modules provide two types of endpoints: Store APIs and Admin APIs. The Store APIs are typically accessed from the storefront. For example, you can use the Store APIs to show customers available products or implement a cart and checkout flow.
|
||||
|
||||
The Admin APIs are typically accessed from an admin dashboard. For example, you can use the Admin APIs to allow admins to manage the store’s data such as products, orders, and so on.
|
||||
|
||||
<DocCardList colSize={6} items={[
|
||||
{
|
||||
type: 'link',
|
||||
href: 'https://docs.medusajs.com/api/store',
|
||||
label: 'Store APIs',
|
||||
customProps: {
|
||||
icon: Icons['server-solid'],
|
||||
description: 'Check out available Store REST APIs.'
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'link',
|
||||
href: 'https://docs.medusajs.com/api/admin',
|
||||
label: 'Admin APIs',
|
||||
customProps: {
|
||||
icon: Icons['server-solid'],
|
||||
description: 'Check out available Admin REST APIs.'
|
||||
}
|
||||
},
|
||||
]} />
|
||||
|
||||
---
|
||||
|
||||
## Custom Development
|
||||
|
||||
Aside from using the endpoints that commerce modules, developers can create their own REST APIs either directly in the Medusa Backend, in a plugin, or in a custom commerce module.
|
||||
|
||||
:::tip
|
||||
|
||||
As the core Medusa package is completely customizable, developers can also extend the functionality even further to implement GraphQL endpoints.
|
||||
|
||||
:::
|
||||
|
||||
<DocCardList colSize={6} items={[
|
||||
{
|
||||
type: 'link',
|
||||
href: '/development/endpoints/create',
|
||||
label: 'Create an Endpoint',
|
||||
customProps: {
|
||||
icon: Icons['academic-cap-solid'],
|
||||
description: 'Learn how to create an endpoint in Medusa.'
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'link',
|
||||
href: '/development/endpoints/add-middleware',
|
||||
label: 'Add a Middleware',
|
||||
customProps: {
|
||||
icon: Icons['academic-cap-solid'],
|
||||
description: 'Learn how to add a middleware in Medusa.'
|
||||
}
|
||||
},
|
||||
]} />
|
||||
@@ -124,21 +124,21 @@ You can now use your extended entity throughout your commerce application.
|
||||
|
||||
---
|
||||
|
||||
## Access Custom Attributes and Relations in Core Endpoints
|
||||
## Access Custom Attributes and Relations in Core API Routes
|
||||
|
||||
### Request Parameters
|
||||
|
||||
In most cases, after you extend an entity to add new attributes, you'll likely need to pass these attributes to endpoints defined in the core. By default, this causes an error, as request parameters are validated to ensure only those that are defined are passed to the endpoint.
|
||||
In most cases, after you extend an entity to add new attributes, you'll likely need to pass these attributes to API Routes defined in the core. By default, this causes an error, as request parameters are validated to ensure only those that are defined are passed to the API Route.
|
||||
|
||||
To allow passing your custom attribute, you'll need to [extend the validator](../endpoints/extend-validator.md) of the endpoint.
|
||||
To allow passing your custom attribute, you'll need to [extend the validator](../api-routes/extend-validator.md) of the API Route.
|
||||
|
||||
### Response Fields
|
||||
|
||||
After you add custom attributes, you'll notice that these attributes aren't returned as part of the response fields of core endpoints. Core endpoints have a defined set of fields and relations that can be returned by default in requests.
|
||||
After you add custom attributes, you'll notice that these attributes aren't returned as part of the response fields of core API Routes. Core API Routes have a defined set of fields and relations that can be returned by default in requests.
|
||||
|
||||
To change that and ensure your custom attribute is returned in your request, you can extend the allowed fields of a set of endpoints in a loader file and add your attribute into them.
|
||||
To change that and ensure your custom attribute is returned in your request, you can extend the allowed fields of a set of API Routes in a loader file and add your attribute into them.
|
||||
|
||||
For example, if you added a custom attribute in the `Product` entity and you want to ensure it's returned in all the product's store endpoints (endpoints under the prefix `/store/products`), you can create a file under the `src/loaders` directory in your Medusa backend with the following content:
|
||||
For example, if you added a custom attribute in the `Product` entity and you want to ensure it's returned in all the product's store API Routes (API Routes under the prefix `/store/products`), you can create a file under the `src/loaders` directory in your Medusa backend with the following content:
|
||||
|
||||
```ts title=src/loaders/extend-product-fields.ts
|
||||
export default async function () {
|
||||
@@ -156,10 +156,10 @@ export default async function () {
|
||||
}
|
||||
```
|
||||
|
||||
In the code snippet above, you import `@medusajs/medusa/dist/api/routes/store/products/index`, which is where all the product's store endpoints are exported. In that file, there are the following defined variables:
|
||||
In the code snippet above, you import `@medusajs/medusa/dist/api/routes/store/products/index`, which is where all the product's store API Routes are exported. In that file, there are the following defined variables:
|
||||
|
||||
- `allowedStoreProductsFields`: The fields or attributes of a product that are allowed to be retrieved and returned in the product's store endpoints. This would allow you to pass your custom attribute in the `fields` request parameter of the product's store endpoints.
|
||||
- `defaultStoreProductsFields`: The fields or attributes of a product that are retrieved and returned by default in the product's store endpoints.
|
||||
- `allowedStoreProductsFields`: The fields or attributes of a product that are allowed to be retrieved and returned in the product's store API Routes. This would allow you to pass your custom attribute in the `fields` request parameter of the product's store API Routes.
|
||||
- `defaultStoreProductsFields`: The fields or attributes of a product that are retrieved and returned by default in the product's store API Routes.
|
||||
|
||||
You change the values of these variables and pass the name of your custom attribute. Make sure to change `customAttribute` to the name of your custom attribute.
|
||||
|
||||
@@ -171,10 +171,10 @@ Before you test out the above change, make sure to build your changes before you
|
||||
|
||||
You can also add custom relations by changing the following defined variables:
|
||||
|
||||
- `allowedStoreProductsRelations`: The relations of a product that are allowed to be retrieved and returned in the product's store endpoints. This would allow you to pass your custom relation in the `expand` request parameter of the product's store endpoints.
|
||||
- `defaultStoreProductsRelations`: The relations of a product that are retrieved and returned by default in the product's store endpoints.
|
||||
- `allowedStoreProductsRelations`: The relations of a product that are allowed to be retrieved and returned in the product's store API Routes. This would allow you to pass your custom relation in the `expand` request parameter of the product's store API Routes.
|
||||
- `defaultStoreProductsRelations`: The relations of a product that are retrieved and returned by default in the product's store API Routes.
|
||||
|
||||
If you want to apply this example for a different entity or set of endpoints, you would need to change the import path `@medusajs/medusa/dist/api/routes/store/products/index` to the path of the endpoints you're targeting. You also need to change `allowedStoreProductsFields` and `defaultStoreProductsFields` to the names of the variables in that file, and the same goes for relations. Typically, these names would be of the format `(allowed|default)(Store|Admin)(Entity)(Fields|Relation)`.
|
||||
If you want to apply this example for a different entity or set of API Routes, you would need to change the import path `@medusajs/medusa/dist/api/routes/store/products/index` to the path of the API Routes you're targeting. You also need to change `allowedStoreProductsFields` and `defaultStoreProductsFields` to the names of the variables in that file, and the same goes for relations. Typically, these names would be of the format `(allowed|default)(Store|Admin)(Entity)(Fields|Relation)`.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -83,32 +83,35 @@ After that, you can add your custom methods to the repository. In the example ab
|
||||
|
||||
## Step 3: Use Your Extended Repository
|
||||
|
||||
You can now use your extended repository in other resources such as services or endpoints.
|
||||
You can now use your extended repository in other resources such as services or API Routes.
|
||||
|
||||
Here’s an example of using it in an endpoint:
|
||||
Here’s an example of using it in an API Route:
|
||||
|
||||
```ts
|
||||
```ts title=src/api/store/custom/route.ts
|
||||
import type {
|
||||
MedusaRequest,
|
||||
MedusaResponse,
|
||||
} from "@medusajs/medusa"
|
||||
import ProductRepository from "./path/to/product.ts"
|
||||
import EntityManager from "@medusajs/medusa"
|
||||
import { EntityManager } from "typeorm"
|
||||
|
||||
export default () => {
|
||||
export const GET = async (
|
||||
req: MedusaRequest,
|
||||
res: MedusaResponse
|
||||
) => {
|
||||
// ...
|
||||
|
||||
router.get("/custom-endpoint", (req, res) => {
|
||||
// ...
|
||||
const productRepository: typeof ProductRepository =
|
||||
req.scope.resolve(
|
||||
"productRepository"
|
||||
)
|
||||
const manager: EntityManager = req.scope.resolve("manager")
|
||||
const productRepo = manager.withRepository(
|
||||
productRepository
|
||||
)
|
||||
productRepo.customFunction()
|
||||
|
||||
const productRepository: typeof ProductRepository =
|
||||
req.scope.resolve(
|
||||
"productRepository"
|
||||
)
|
||||
const manager: EntityManager = req.scope.resolve("manager")
|
||||
const productRepo = manager.withRepository(
|
||||
productRepository
|
||||
)
|
||||
productRepo.customFunction()
|
||||
|
||||
// ...
|
||||
})
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -80,10 +80,10 @@ Developers can create custom entities in the Medusa backend, a plugin, or in a m
|
||||
{
|
||||
type: 'link',
|
||||
href: '/development/entities/create',
|
||||
label: 'Create an Endpoint',
|
||||
label: 'Create an API Route',
|
||||
customProps: {
|
||||
icon: Icons['academic-cap-solid'],
|
||||
description: 'Learn how to create endpoints in Medusa.'
|
||||
description: 'Learn how to create API Routes in Medusa.'
|
||||
}
|
||||
},
|
||||
]} />
|
||||
@@ -39,27 +39,26 @@ class PostService extends TransactionBaseService {
|
||||
}
|
||||
```
|
||||
|
||||
Another example is retrieving the default repository of an entity in an endpoint:
|
||||
Another example is retrieving the default repository of an entity in an API Route:
|
||||
|
||||
```ts title=src/api/index.ts
|
||||
```ts title=src/api/store/custom/route.ts
|
||||
import type {
|
||||
MedusaRequest,
|
||||
MedusaResponse,
|
||||
} from "@medusajs/medusa"
|
||||
import { Post } from "../models/post"
|
||||
import { EntityManager } from "typeorm"
|
||||
|
||||
// ...
|
||||
export const GET = async (
|
||||
req: MedusaRequest,
|
||||
res: MedusaResponse
|
||||
) => {
|
||||
const manager: EntityManager = req.scope.resolve("manager")
|
||||
const postRepo = manager.getRepository(Post)
|
||||
|
||||
export default () => {
|
||||
// ...
|
||||
|
||||
storeRouter.get("/posts", async (req, res) => {
|
||||
const manager: EntityManager = req.scope.resolve("manager")
|
||||
const postRepo = manager.getRepository(Post)
|
||||
|
||||
return res.json({
|
||||
posts: await postRepo.find(),
|
||||
})
|
||||
return res.json({
|
||||
posts: await postRepo.find(),
|
||||
})
|
||||
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
@@ -105,35 +104,36 @@ A data source is Typeorm’s connection settings that allows you to connect to y
|
||||
|
||||
## Using Custom Repositories in Other Resources
|
||||
|
||||
### Endpoints
|
||||
### API Routes
|
||||
|
||||
To access a custom repository within an endpoint, use the `req.scope.resolve` method. For example:
|
||||
To access a custom repository within an API Route, use the `MedusaRequest` object's `scope.resolve` method.
|
||||
|
||||
```ts title=src/api/index.ts
|
||||
For example:
|
||||
|
||||
```ts title=src/store/custom/route.ts
|
||||
import type {
|
||||
MedusaRequest,
|
||||
MedusaResponse,
|
||||
} from "@medusajs/medusa"
|
||||
import { PostRepository } from "../repositories/post"
|
||||
import { EntityManager } from "typeorm"
|
||||
|
||||
// ...
|
||||
export const GET = async (
|
||||
req: MedusaRequest,
|
||||
res: MedusaResponse
|
||||
) => {
|
||||
const postRepository: typeof PostRepository =
|
||||
req.scope.resolve("postRepository")
|
||||
const manager: EntityManager = req.scope.resolve("manager")
|
||||
const postRepo = manager.withRepository(postRepository)
|
||||
|
||||
export default () => {
|
||||
// ...
|
||||
|
||||
storeRouter.get("/posts", async (req, res) => {
|
||||
const postRepository: typeof PostRepository =
|
||||
req.scope.resolve("postRepository")
|
||||
const manager: EntityManager = req.scope.resolve("manager")
|
||||
const postRepo = manager.withRepository(postRepository)
|
||||
|
||||
return res.json({
|
||||
posts: await postRepo.find(),
|
||||
})
|
||||
return res.json({
|
||||
posts: await postRepo.find(),
|
||||
})
|
||||
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
You can learn more about endpoints [here](../endpoints/overview.mdx).
|
||||
You can learn more about API Route [here](../api-routes/overview.mdx).
|
||||
|
||||
### Services and Subscribers
|
||||
|
||||
@@ -336,4 +336,4 @@ You can later retrieve that entity by passing the `withDeleted` option to method
|
||||
const posts = await postRepository.find({
|
||||
withDeleted: true,
|
||||
})
|
||||
```
|
||||
```
|
||||
|
||||
@@ -2218,7 +2218,7 @@ Triggered when a product and data associated with it (options, variant orders, e
|
||||
|
||||
The entire product passed as an object. You can refer to the [Product entity](../../references/entities/classes/Product.md) for an idea of what fields to expect.
|
||||
|
||||
In one case, when the `/admin/products/{id}` endpoint is used to update the product, the payload is an object of the following format:
|
||||
In one case, when the `/admin/products/{id}` API Route is used to update the product, the payload is an object of the following format:
|
||||
|
||||
```js noReport noCopy
|
||||
{
|
||||
|
||||
@@ -13,7 +13,7 @@ In this document, you’ll learn what feature flags in Medusa.
|
||||
|
||||
Feature flags are used in Medusa to guard beta features that aren’t ready for live and production applications. This allows the Medusa team to keep publishing releases more frequently, while also working on necessary future features behind the scenes. To use these beta features, you must enable their feature flags.
|
||||
|
||||
If a feature is guarded by a flag, entities, migrations, endpoints, and other resources associated with that feature are guarded by that flag as well. So, these resources will only be available to use in Medusa if you have enabled the associated feature flag.
|
||||
If a feature is guarded by a flag, entities, migrations, API Routes, and other resources associated with that feature are guarded by that flag as well. So, these resources will only be available to use in Medusa if you have enabled the associated feature flag.
|
||||
|
||||
You can view a list of available feature flags that you can toggle in [the Beta Features documentation](../../beta.md).
|
||||
|
||||
|
||||
@@ -74,7 +74,7 @@ You can learn more about migrations in this documentation.
|
||||
|
||||
Disabling feature flags follows the same process as enabling the feature flags. All you have to do is change the value in the environment variables or the backend configurations to `false`.
|
||||
|
||||
Once you disable a feature flag, all endpoints, entities, services, or other related classes and functionalities are disabled.
|
||||
Once you disable a feature flag, all API Routes, entities, services, or other related classes and functionalities are disabled.
|
||||
|
||||
### Revert Migrations
|
||||
|
||||
|
||||
@@ -438,7 +438,7 @@ Run your backend to test it out:
|
||||
npx medusa develop
|
||||
```
|
||||
|
||||
Then, try uploading a file, for example, using the [Upload File endpoint](https://docs.medusajs.com/api/admin#uploads_postuploads). The file should be uploaded based on the logic you’ve implemented.
|
||||
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
|
||||
|
||||
@@ -452,15 +452,17 @@ Since the file is uploaded to a local directory `uploads`, you need to configure
|
||||
|
||||
To do that, create the file `src/api/index.ts` with the following content:
|
||||
|
||||
```ts
|
||||
```ts title=src/api/middlewares.ts
|
||||
import type { MiddlewaresConfig } from "@medusajs/medusa"
|
||||
import express from "express"
|
||||
|
||||
export default () => {
|
||||
const app = express.Router()
|
||||
|
||||
app.use(`/uploads`, express.static(uploadDir))
|
||||
|
||||
return app
|
||||
export const config: MiddlewaresConfig = {
|
||||
routes: [
|
||||
{
|
||||
matcher: "/uploads",
|
||||
middlewares: [express.static(uploadDir)],
|
||||
},
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -8,20 +8,20 @@ In this document, you'll get an overview of Medusa's architecture to better unde
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
Medusa's core package `@medusajs/medusa` is a Node.js backend built on top of [Express](https://expressjs.com/). It combines all the [Commerce Modules](../../modules/overview.mdx) that Medusa provides. Commerce Modules are ecommerce features that can be used as building blocks in an ecommerce ecosystem. Product is an example of a Commerce Module.
|
||||
Medusa's core package `@medusajs/medusa` is a Node.js headless server. It combines all the [Commerce Modules](../../modules/overview.mdx) that Medusa provides. Commerce Modules are ecommerce features that can be used as building blocks in an ecommerce ecosystem. Product is an example of a Commerce Module.
|
||||
|
||||

|
||||

|
||||
|
||||
The backend connects to a database, such as [PostgreSQL](https://www.postgresql.org/), to store the ecommerce store’s data. The tables in that database are represented by [Entities](../entities/overview.mdx), built on top of [Typeorm](https://typeorm.io/). Entities can also be reflected in the database using [Migrations](../entities/migrations/overview.mdx).
|
||||
|
||||
The retrieval, manipulation, and other utility methods related to that entity are created inside a [Service](../services/overview.mdx). Services are TypeScript or JavaScript classes that, along with other resources, can be accessed throughout the Medusa backend through [dependency injection](./dependency-injection.md).
|
||||
|
||||
The backend does not have any tightly-coupled frontend. Instead, it exposes [Endpoints](../endpoints/overview.mdx) which are REST APIs that frontends such as an admin or a storefront can use to communicate with the backend. Endpoints are [Express routes](https://expressjs.com/en/guide/routing.html).
|
||||
The backend doesn't have any tightly-coupled frontend. Instead, it exposes [API Routes](../api-routes/overview.mdx) which are REST APIs that frontends such as an admin or a storefront can use to communicate with the backend.
|
||||
|
||||
Medusa also uses an [Events Architecture](../events/index.mdx) to trigger and handle events. Events are triggered when a specific action occurs, such as when an order is placed. To manage this events system, Medusa connects to a service that implements a pub/sub model, such as [Redis](https://redis.io/).
|
||||
|
||||
Events can be handled using [Subscribers](../events/subscribers.mdx). Subscribers are TypeScript or JavaScript classes that add their methods as handlers for specific events. These handler methods are only executed when an event is triggered.
|
||||
|
||||
You can create any of the resources in the backend’s architecture, such as entities, endpoints, services, and more, as part of your custom development without directly modifying the backend itself. The Medusa backend uses [loaders](../loaders/overview.mdx) to load the backend’s resources, as well as your custom resources and resources in [Plugins](../plugins/overview.mdx).
|
||||
You can create any of the resources in the backend’s architecture, such as entities, API Routes, services, and more, as part of your custom development without directly modifying the backend itself. The Medusa backend uses [loaders](../loaders/overview.mdx) to load the backend’s resources, as well as your custom resources and resources in [Plugins](../plugins/overview.mdx).
|
||||
|
||||
You can package your customizations into Plugins to reuse them in different Medusa backends or publish them for others to use. You can also install existing plugins into your Medusa backend.
|
||||
|
||||
@@ -16,7 +16,7 @@ Generally, all resources are registered in a container. Then, whenever a class d
|
||||
|
||||
### Medusa’s Dependency Container
|
||||
|
||||
Medusa uses a dependency container to register essential resources of the backend. You can then access these resources in classes and endpoints using the dependency container.
|
||||
Medusa uses a dependency container to register essential resources of the backend. You can then access these resources in classes and API Routes using the dependency container.
|
||||
|
||||
For example, if you create a custom service, you can access any other service registered in Medusa in your service’s constructor. That includes Medusa’s core services, services defined in plugins, or other services that you create on your backend.
|
||||
|
||||
@@ -28,7 +28,7 @@ To manage dependency injections, Medusa uses [Awilix](https://github.com/jeffijo
|
||||
|
||||
When you run the Medusa backend, a container of the type `MedusaContainer` is created. This type extends the [AwilixContainer](https://github.com/jeffijoe/awilix#the-awilixcontainer-object) object.
|
||||
|
||||
The backend then registers all important resources in the container, which makes them accessible in classes and endpoints.
|
||||
The backend then registers all important resources in the container, which makes them accessible in classes and API Routes.
|
||||
|
||||
---
|
||||
|
||||
@@ -695,11 +695,11 @@ Its camel-case name.
|
||||
|
||||
## Resolve Resources
|
||||
|
||||
This section covers how to resolve resources from the dependency container to use them in endpoints and classes in general.
|
||||
This section covers how to resolve resources from the dependency container to use them in API Routes and classes in general.
|
||||
|
||||
### In Endpoints
|
||||
### In API Routes
|
||||
|
||||
To resolve resources, such as services, in endpoints, use the `req.scope.resolve` method. The method receives the registration name of the resource as a parameter.
|
||||
To resolve resources, such as services, in API Routes, use the `MedusaRequest` object's `scope.resolve` method. The method receives the registration name of the resource as a parameter.
|
||||
|
||||
For example:
|
||||
|
||||
@@ -707,7 +707,7 @@ For example:
|
||||
const logger = req.scope.resolve("logger")
|
||||
```
|
||||
|
||||
Please note that in endpoints some resources, such as repositories, are not available. Refer to the [repositories](../entities/repositories.md) documentation to learn how you can load them.
|
||||
Please note that in API Routes some resources, such as repositories, aren't available. Refer to the [repositories](../entities/repositories.md#api-routes) documentation to learn how you can load them.
|
||||
|
||||
### In Classes
|
||||
|
||||
|
||||
@@ -104,7 +104,7 @@ yarn test
|
||||
|
||||
### Run API Integration Tests
|
||||
|
||||
API integration tests are used to test out Medusa’s core endpoints.
|
||||
API integration tests are used to test out Medusa’s core API Routes.
|
||||
|
||||
To run the API integration tests, run the following command in the root directory of the repository:
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ In this document, you'll learn what an idempotency key is in Medusa.
|
||||
|
||||
## Overview
|
||||
|
||||
An Idempotency Key is a unique, randomly generated key associated with an operation, such as the cart completion process. The idempotency key can be passed in the header of a request to an endpoint. This allows you to safely retry requests without accidentally performing the same operation twice.
|
||||
An Idempotency Key is a unique, randomly generated key associated with an operation, such as the cart completion process. The idempotency key can be passed in the header of a request to an API Route. This allows you to safely retry requests without accidentally performing the same operation twice.
|
||||
|
||||
For example, if a connection error occurs while the customer is completing their cart and placing an order, you can retry from the last recovery point before the error occurred.
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ In this document, you'll learn how to use the `IdempotencyKeyService`.
|
||||
|
||||
## Overview
|
||||
|
||||
You can use the `IdempotencyKeyService` within your custom development to ensure that your custom endpoints and operations can be safely retried or continued if an error occurs. This guide is also useful if you're overriding an existing feature in Medusa that uses the `IdempotencyKeyService` and you want to maintain its usage, such as if you're overriding the cart completion strategy.
|
||||
You can use the `IdempotencyKeyService` within your custom development to ensure that your custom API Routes and operations can be safely retried or continued if an error occurs. This guide is also useful if you're overriding an existing feature in Medusa that uses the `IdempotencyKeyService` and you want to maintain its usage, such as if you're overriding the cart completion strategy.
|
||||
|
||||
The `IdempotencyKeyService` includes methods that can be used to create and update idempotency keys, among other functionalities.
|
||||
|
||||
@@ -17,18 +17,30 @@ The `IdempotencyKeyService` includes methods that can be used to create and upda
|
||||
|
||||
## Create Idempotency Key
|
||||
|
||||
You can create an idempotency key within an endpoint using the `create` method of the `IdempotencyKeyService`:
|
||||
You can create an idempotency key within an API Route using the `create` method of the `IdempotencyKeyService`:
|
||||
|
||||
```ts
|
||||
router.post("/custom-route", async (req, res) => {
|
||||
```ts title=src/api/store/custom/route.ts
|
||||
import type {
|
||||
MedusaRequest,
|
||||
MedusaResponse,
|
||||
} from "@medusajs/medusa"
|
||||
import { IdempotencyKeyService } from "@medusajs/medusa"
|
||||
|
||||
export const POST = async (
|
||||
req: MedusaRequest,
|
||||
res: MedusaResponse
|
||||
) => {
|
||||
// ...
|
||||
const idempotencyKeyService = req.scope.resolve<
|
||||
IdempotencyKeyService
|
||||
>("idempotencyKeyService")
|
||||
const idempotencyKey = await idempotencyKeyService.create({
|
||||
request_method: req.method,
|
||||
request_params: req.params,
|
||||
request_path: req.path,
|
||||
})
|
||||
// ...
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
The method requires as a parameter an object having the following properties:
|
||||
@@ -41,9 +53,23 @@ The method handles generating the idempotency key value and saving the idempoten
|
||||
|
||||
Alternatively, you can use the `initializeRequest` method that allows you to retrieve an idempotency key based on the value passed in the `Idempotency-Key` header of the request if it exists, or create a new key otherwise. For example:
|
||||
|
||||
```ts
|
||||
router.post("/custom-route", async (req, res) => {
|
||||
```ts title=src/api/store/custom/route.ts
|
||||
import type {
|
||||
MedusaRequest,
|
||||
MedusaResponse,
|
||||
} from "@medusajs/medusa"
|
||||
import {
|
||||
IdempotencyKeyService,
|
||||
} from "@medusajs/medusa"
|
||||
|
||||
export const POST = async (
|
||||
req: MedusaRequest,
|
||||
res: MedusaResponse
|
||||
) => {
|
||||
// ...
|
||||
const idempotencyKeyService = req.scope.resolve<
|
||||
IdempotencyKeyService
|
||||
>("idempotencyKeyService")
|
||||
const headerKey = req.get("Idempotency-Key") || ""
|
||||
|
||||
const idempotencyKey = await idempotencyKeyService
|
||||
@@ -54,7 +80,7 @@ router.post("/custom-route", async (req, res) => {
|
||||
req.path
|
||||
)
|
||||
// ...
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
The method requires the following parameters:
|
||||
|
||||
@@ -200,12 +200,12 @@ The `to` and `data` properties are used in the `NotificationService` in Medusa
|
||||
|
||||
### resendNotification
|
||||
|
||||
Using the [Resend Notification endpoint](https://docs.medusajs.com/api/admin#notifications_postnotificationsnotificationresend), an admin user can resend a Notification to the customer. The [`NotificationService`](../../references/services/classes/NotificationService.md) in Medusa’s core then executes the `resendNotification` method in your Notification Provider.
|
||||
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/NotificationService.md) 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 endpoint 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.
|
||||
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/NotificationService.md#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:
|
||||
@@ -323,21 +323,21 @@ After placing an order, you can see in your console the message “Notification
|
||||
|
||||
## 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 admin endpoint](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.
|
||||
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](https://docs.medusajs.com/api/admin#auth_postauth) endpoint to get authenticated.
|
||||
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](https://docs.medusajs.com/api/admin#notifications_postnotificationsnotificationresend) endpoint 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.
|
||||
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 endpoint, 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.
|
||||
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.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -86,7 +86,7 @@ With Medusa you can create notifications as a reaction to a wide spectrum of eve
|
||||
|
||||
An example of a flow that can be implemented using Medusa's Notification API is automated return flows:
|
||||
|
||||
- A customer requests a return by sending a `POST` request to the `/store/returns` endpoint.
|
||||
- A customer requests a return by sending a `POST` request to the `/store/returns` API Route.
|
||||
- The Notification Provider listens to the `order.return_requested` event and sends an email to the customer with a return invoice and return label generated by the Fulfillment Provider.
|
||||
- The customer returns the items triggering the `return.received` event.
|
||||
- The Notification Provider listens to the `return.received` event and sends an email to the customer with confirmation that their items have been received and that a refund has been issued.
|
||||
|
||||
@@ -36,7 +36,14 @@ When you build a commerce application with Medusa, you’ll typically interact w
|
||||
For example, imagine an Inventory module that contains lightweight logic to increment and decrement stock levels for a Stock-Keeping Unit (SKU). In a commerce application, you typically want to associate the stock levels with a specific product. Medusa offers both an Inventory module and a Product module, and the core package creates associations between these modules and executing the related business logic. So, the core package contains code similar to this:
|
||||
|
||||
```ts
|
||||
async function handler(req, res) {
|
||||
import type {
|
||||
MedusaRequest,
|
||||
MedusaResponse,
|
||||
} from "@medusajs/medusa"
|
||||
export const POST = async (
|
||||
req: MedusaRequest,
|
||||
res: MedusaResponse
|
||||
) => {
|
||||
// ...
|
||||
|
||||
// associate a product with an inventory item
|
||||
@@ -57,7 +64,7 @@ async function handler(req, res) {
|
||||
|
||||
The goal of orchestrating the modules is to expose an API that client applications, like websites or apps, can consume. By default, Medusa’s core package exposes a REST API that offers commerce functionalities similar to what other platforms give you.
|
||||
|
||||
The core package also holds the logic that allows developers to extend and add custom endpoints, among other available customizations.
|
||||
The core package also holds the logic that allows developers to extend and add custom API Routes, among other available customizations.
|
||||
|
||||
---
|
||||
|
||||
@@ -111,8 +118,8 @@ These concepts will guide you through your development and building customizatio
|
||||
},
|
||||
{
|
||||
type: 'link',
|
||||
href: '/development/endpoints/overview',
|
||||
label: 'Endpoints',
|
||||
href: '/development/api-routes/overview',
|
||||
label: 'API Routes',
|
||||
customProps: {
|
||||
icon: Icons['academic-cap-solid'],
|
||||
description: 'REST APIs that frontends consume to communicate with the backend.'
|
||||
@@ -194,9 +201,9 @@ By installing a Module in your project and expose its APIs based on the framewor
|
||||
|
||||
Developers can use Medusa’s toolkit to create their ecommerce system. With the use of the [create-medusa-app](../create-medusa-app.mdx) command, developers can set up a Medusa Backend, Medusa admin, and a storefront.
|
||||
|
||||

|
||||

|
||||
|
||||
Developers can still benefit from customization opportunities here that Medusa provides. This includes creating resources such as endpoints and services, creating plugins, integrating third-party services, create a custom storefront, and more.
|
||||
Developers can still benefit from customization opportunities here that Medusa provides. This includes creating resources such as API Routes and services, creating plugins, integrating third-party services, create a custom storefront, and more.
|
||||
|
||||
### Your Own Use Case
|
||||
|
||||
|
||||
@@ -196,17 +196,11 @@ Make sure to delete these files if you're not using them in your plugin.
|
||||
|
||||
### Plugin Structure
|
||||
|
||||
While developing your plugin, you can create your TypeScript or JavaScript files under the `src` directory. This includes creating services, endpoints, migrations, and other resources.
|
||||
While developing your plugin, you can create your TypeScript or JavaScript files under the `src` directory. This includes creating services, API Routes, migrations, and other resources.
|
||||
|
||||
However, before you test the changes on a Medusa backend or publish your plugin, you must transpile your files and move them either to a `dist` directory or to the root of the plugin's directory.
|
||||
|
||||
For example, if you have an endpoint in `src/api/index.js`, after running the `build` or `watch` commands [as defined earlier](#recommended-change-scripts), the file should be transpiled into `dist/api/index.js` in your plugin's root. You can alternative transpile them into the `api/index.js` in your plugin's root.
|
||||
|
||||
:::note
|
||||
|
||||
It was previously required to output your files into the root of the plugin's directory (for example, `api/index.js` instead of `dist/api/index.js`). As of v1.8, you can either have your files in the root of the directory or under the `dist` directory.
|
||||
|
||||
:::
|
||||
For example, if you have an API Route in `src/api/store/custom/route.ts`, after running the `build` or `watch` commands [as defined earlier](#recommended-change-scripts), the file should be transpiled into `dist/api/store/custom/route.ts` in your plugin's root. You can alternative transpile them into the `api/store/custom/route.ts` in your plugin's root.
|
||||
|
||||
### Development Resources
|
||||
|
||||
@@ -233,11 +227,11 @@ This guide doesn't cover how to create different files and components. If you’
|
||||
},
|
||||
{
|
||||
type: 'link',
|
||||
href: '/development/endpoints/create',
|
||||
label: 'Create an Endpoint',
|
||||
href: '/development/api-routes/create',
|
||||
label: 'Create an API Route',
|
||||
customProps: {
|
||||
icon: Icons['academic-cap-solid'],
|
||||
description: 'Learn how to create an endpoint.'
|
||||
description: 'Learn how to create an API Route.'
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -364,27 +358,6 @@ class MyService extends TransactionBaseService {
|
||||
}
|
||||
```
|
||||
|
||||
You can also access the options in your plugin's endpoints. The second parameter that the function declared in `src/api/index.ts` receives is an object including your plugin's configurations.
|
||||
|
||||
For example:
|
||||
|
||||
```js title=src/api/index.ts
|
||||
// in an endpoint in your plugin
|
||||
export default (rootDirectory, options) => {
|
||||
// options contain the plugin options
|
||||
const router = Router()
|
||||
|
||||
router.get("/hello-world", (req, res) => {
|
||||
res.json({
|
||||
message:
|
||||
`Welcome to ${options.name ? options.name : "Medusa"}!`,
|
||||
})
|
||||
})
|
||||
|
||||
return router
|
||||
}
|
||||
```
|
||||
|
||||
:::tip
|
||||
|
||||
Make sure to include in the README of your plugin the options that can be passed to a plugin.
|
||||
|
||||
+8
-8
@@ -61,7 +61,7 @@ You can learn more about [authenticating as an admin user in the API reference](
|
||||
|
||||
## List Publishable API Keys
|
||||
|
||||
You can retrieve a list of publishable API keys by sending a request to the [List Publishable API Keys](https://docs.medusajs.com/api/admin#publishable-api-keys_getpublishableapikeys) endpoint:
|
||||
You can retrieve a list of publishable API keys by sending a request to the [List Publishable API Keys route](https://docs.medusajs.com/api/admin#publishable-api-keys_getpublishableapikeys):
|
||||
|
||||
<Tabs groupId="request-type" isCodeTabs={true}>
|
||||
<TabItem value="client" label="Medusa JS Client" default>
|
||||
@@ -152,7 +152,7 @@ You can learn more about pagination in the [API reference](https://docs.medusajs
|
||||
|
||||
## Create a Publishable API Key
|
||||
|
||||
You can create a publishable API key by sending a request to the [Create Publishable API Key](https://docs.medusajs.com/api/admin#publishable-api-keys_postpublishableapikeys) endpoint:
|
||||
You can create a publishable API key by sending a request to the [Create Publishable API Key route](https://docs.medusajs.com/api/admin#publishable-api-keys_postpublishableapikeys):
|
||||
|
||||
<Tabs groupId="request-type" isCodeTabs={true}>
|
||||
<TabItem value="client" label="Medusa JS Client" default>
|
||||
@@ -231,7 +231,7 @@ It returns the created publishable API key in the response.
|
||||
|
||||
## Update a Publishable API Key
|
||||
|
||||
You can update a publishable API key’s details by sending a request to the [Update Publishable API Key](https://docs.medusajs.com/api/admin#publishable-api-keys_postpublishableapikyspublishableapikey) endpoint:
|
||||
You can update a publishable API key’s details by sending a request to the [Update Publishable API Key route](https://docs.medusajs.com/api/admin#publishable-api-keys_postpublishableapikyspublishableapikey):
|
||||
|
||||
<Tabs groupId="request-type" isCodeTabs={true}>
|
||||
<TabItem value="client" label="Medusa JS Client" default>
|
||||
@@ -316,7 +316,7 @@ This request returns the update publishable API key object in the response.
|
||||
|
||||
Revoking a publishable API key does not remove it, but does not allow using it in future requests.
|
||||
|
||||
You can revoke a publishable API key by sending a request to the [Revoke Publishable API Key](https://docs.medusajs.com/api/admin#publishable-api-keys_postpublishableapikeyspublishableapikeyrevoke) endpoint:
|
||||
You can revoke a publishable API key by sending a request to the [Revoke Publishable API Key route](https://docs.medusajs.com/api/admin#publishable-api-keys_postpublishableapikeyspublishableapikeyrevoke):
|
||||
|
||||
<Tabs groupId="request-type" isCodeTabs={true}>
|
||||
<TabItem value="client" label="Medusa JS Client" default>
|
||||
@@ -386,7 +386,7 @@ This request requires the ID of the publishable API key as a path parameter. It
|
||||
|
||||
## Delete a Publishable API Key
|
||||
|
||||
You can delete a publishable API key by sending a request to the [Delete Publishable API Key](https://docs.medusajs.com/api/admin#publishable-api-keys_deletepublishableapikeyspublishableapikey) endpoint:
|
||||
You can delete a publishable API key by sending a request to the [Delete Publishable API Key route](https://docs.medusajs.com/api/admin#publishable-api-keys_deletepublishableapikeyspublishableapikey):
|
||||
|
||||
<Tabs groupId="request-type" isCodeTabs={true}>
|
||||
<TabItem value="client" label="Medusa JS Client" default>
|
||||
@@ -463,7 +463,7 @@ This section covers how to manage sales channels in a publishable API key. This
|
||||
|
||||
### List Sales Channels of a Publishable API Key
|
||||
|
||||
You can retrieve the list of sales channels associated with a publishable API key by sending a request to the [List Sales Channels](https://docs.medusajs.com/api/admin#sales-channels_getsaleschannels) endpoint:
|
||||
You can retrieve the list of sales channels associated with a publishable API key by sending a request to the [List Sales Channels API Route](https://docs.medusajs.com/api/admin#sales-channels_getsaleschannels):
|
||||
|
||||
<Tabs groupId="request-type" isCodeTabs={true}>
|
||||
<TabItem value="client" label="Medusa JS Client" default>
|
||||
@@ -546,7 +546,7 @@ It returns an array of sales channels associated with the publishable API key in
|
||||
|
||||
### Add Sales Channels to Publishable API Key
|
||||
|
||||
You can add a sales channel to a publishable API key by sending a request to the [Add Sales Channels](https://docs.medusajs.com/api/admin#publishable-api-keys_postpublishableapikeysaleschannelschannelsbatch) endpoint:
|
||||
You can add a sales channel to a publishable API key by sending a request to the [Add Sales Channels API Route](https://docs.medusajs.com/api/admin#publishable-api-keys_postpublishableapikeysaleschannelschannelsbatch):
|
||||
|
||||
<Tabs groupId="request-type" isCodeTabs={true}>
|
||||
<TabItem value="client" label="Medusa JS Client" default>
|
||||
@@ -656,7 +656,7 @@ This request returns the updated publishable API key in the response.
|
||||
|
||||
### Delete Sales Channels from a Publishable API Key
|
||||
|
||||
You can delete a sales channel from a publishable API key by sending a request to the [Delete Sales Channels](https://docs.medusajs.com/api/admin#publishable-api-keys_deletepublishableapikeysaleschannelschannelsbatch) endpoint:
|
||||
You can delete a sales channel from a publishable API key by sending a request to the [Delete Sales Channels API Route](https://docs.medusajs.com/api/admin#publishable-api-keys_deletepublishableapikeysaleschannelschannelsbatch):
|
||||
|
||||
<Tabs groupId="request-type" isCodeTabs={true}>
|
||||
<TabItem value="client" label="Medusa JS Client" default>
|
||||
|
||||
@@ -13,7 +13,7 @@ In this document, you’ll learn about Publishable API Keys and their architectu
|
||||
|
||||
While using Medusa’s APIs, you might have to pass some query parameters for certain resources with every or most requests.
|
||||
|
||||
Taking Sales Channels as an example, you have to pass the Sales Channel’s ID as a query parameter to all the necessary endpoints, such as the List Products endpoint.
|
||||
Taking Sales Channels as an example, you have to pass the Sales Channel’s ID as a query parameter to all the necessary API Routes, such as the List Products API Route.
|
||||
|
||||
This is a tedious and error-prone process. This is where Publishable API Keys are useful.
|
||||
|
||||
|
||||
+2
-2
@@ -12,9 +12,9 @@ In this document, you'll learn how to use Publishable API Keys in client request
|
||||
|
||||
:::
|
||||
|
||||
## Default Behaviour In Product Store Endpoints
|
||||
## Default Behaviour In Product Store API Routes
|
||||
|
||||
If you don't pass a publishable API Key for the store endpoints `/store/products` and `/store/products/{product_id}`, the default sales channel of the store is assigned to the request.
|
||||
If you don't pass a publishable API Key for the store API Routes `/store/products` and `/store/products/{product_id}`, the default sales channel of the store is assigned to the request.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -321,11 +321,11 @@ class MySearchService extends AbstractSearchService {
|
||||
|
||||
This method is used to search through an index by a query.
|
||||
|
||||
In the Medusa backend, this method is used within the [Search Products endpoint](https://docs.medusajs.com/api/store#products_postproductssearch) to retrieve the search results.
|
||||
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 endpoint, the index is the default index defined in the `IndexName` static property of the `ProductService`, which is `products`.
|
||||
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.
|
||||
|
||||
@@ -406,7 +406,7 @@ Run your backend to test it out:
|
||||
npx medusa develop
|
||||
```
|
||||
|
||||
You can then send a request to the [Search Products endpoint](https://docs.medusajs.com/api/store#products_postproductssearch) to see if your search service returns any results.
|
||||
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.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -11,11 +11,11 @@ In this document, you’ll learn what a search service is and how it’s used in
|
||||
|
||||
## Overview
|
||||
|
||||
A search service is used to manage search indices of searchable items, such as products, and providing results for search operations. Although the Medusa core provides basic search functionalities through its endpoints, a search service allows you to integrate third-party services for an optimized search experience and rich search functionalities.
|
||||
A search service is used to manage search indices of searchable items, such as products, and providing results for search operations. Although the Medusa core provides basic search functionalities through its API Routes, a search service allows you to integrate third-party services for an optimized search experience and rich search functionalities.
|
||||
|
||||
A search service is a service class that is defined in a TypeScript or JavaScript file, which is created in the `src/services` directory of your Medusa backend codebase or plugin. The class must extend the `AbstractSearchService` class imported from the `@medusajs/utils` package.
|
||||
|
||||
Using the [dependency container and injection](../fundamentals/dependency-injection.md), the Medusa backend will then use and resolve the search service within the backend’s search operations, such as when the [Search Products](https://docs.medusajs.com/api/store#products_postproductssearch) endpoint is used. You can also [resolve the service](../services/create-service.mdx#use-a-service) within your resources to trigger the search where necessary.
|
||||
Using the [dependency container and injection](../fundamentals/dependency-injection.md), the Medusa backend will then use and resolve the search service within the backend’s search operations, such as when the [Search Products API Route](https://docs.medusajs.com/api/store#products_postproductssearch) is used. You can also [resolve the service](../services/create-service.mdx#use-a-service) within your resources to trigger the search where necessary.
|
||||
|
||||
Medusa provides official plugins that you can install and use in your Medusa backend. Check out available search plugins [here](../../plugins/search/index.mdx).
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
---
|
||||
description: 'Learn how to create a service in Medusa. This guide also includes how to use services in other services, subscribers, and endpoints.'
|
||||
addHowToData: true
|
||||
---
|
||||
|
||||
@@ -18,7 +17,7 @@ To create a service, create a TypeScript or JavaScript file in `src/services` to
|
||||
|
||||
For example, if you want to create a service `PostService`, eventually registered as `postService`, create the file `post.ts` in `src/services` with the following content:
|
||||
|
||||
```ts title=/src/services/post.ts
|
||||
```ts title=src/services/post.ts
|
||||
import { TransactionBaseService } from "@medusajs/medusa"
|
||||
|
||||
class PostService extends TransactionBaseService {
|
||||
@@ -54,7 +53,7 @@ As the service extends the `TransactionBaseService` class, all resources registe
|
||||
|
||||
So, if you want your service to use another service, add it as part of your constructor’s dependencies and set it to a field inside your service’s class:
|
||||
|
||||
```ts title=/src/services/post.ts
|
||||
```ts title=src/services/post.ts
|
||||
import { ProductService } from "@medusajs/medusa"
|
||||
import { PostRepository } from "../repositories/post"
|
||||
|
||||
@@ -71,7 +70,7 @@ class PostService extends TransactionBaseService {
|
||||
|
||||
Then, you can use that service anywhere in your custom service. For example:
|
||||
|
||||
```ts title=/src/services/post.ts
|
||||
```ts title=src/services/post.ts
|
||||
class PostService extends TransactionBaseService {
|
||||
// ...
|
||||
async getProductCount() {
|
||||
@@ -92,7 +91,7 @@ However, to actually get an instance of the repository within the service's meth
|
||||
|
||||
For example:
|
||||
|
||||
```ts title=/src/services/post.ts
|
||||
```ts title=src/services/post.ts
|
||||
import { PostRepository } from "../repositories/post"
|
||||
|
||||
class PostService extends TransactionBaseService {
|
||||
@@ -132,7 +131,7 @@ The data returned by the function passed as a parameter to the `atomicPhase_` me
|
||||
|
||||
For example, the `PostService`'s `create` method with the `atomicPhase_` method:
|
||||
|
||||
```ts title=/src/services/post.ts
|
||||
```ts title=src/services/post.ts
|
||||
class PostService extends TransactionBaseService {
|
||||
protected postRepository_: typeof PostRepository
|
||||
// ...
|
||||
@@ -170,7 +169,7 @@ There are three lifetime types:
|
||||
|
||||
You can set the lifetime of your service by setting the `LIFE_TIME` static property:
|
||||
|
||||
```ts title=/src/services/post.ts
|
||||
```ts title=src/services/post.ts
|
||||
import { TransactionBaseService } from "@medusajs/medusa"
|
||||
import { Lifetime } from "awilix"
|
||||
|
||||
@@ -189,7 +188,7 @@ Within your service, you may need to access the Medusa configuration exported fr
|
||||
|
||||
For example:
|
||||
|
||||
```ts title=/src/services/post.ts
|
||||
```ts title=src/services/post.ts
|
||||
import {
|
||||
ConfigModule,
|
||||
TransactionBaseService,
|
||||
@@ -218,7 +217,7 @@ export default PostService
|
||||
|
||||
## Pagination, Filtering, and Relations
|
||||
|
||||
Often, your service will provide methods that retrieve a list of items, which can be used by endpoints. In these methods, it can be helpful to provide filtering and pagination utilities that can be used by endpoints or any other resources utilizing this service.
|
||||
Often, your service will provide methods that retrieve a list of items, which can be used by API Routes. In these methods, it can be helpful to provide filtering and pagination utilities that can be used by API Routes or any other resources utilizing this service.
|
||||
|
||||
The `@medusajs/medusa` package provides the following generic types that you can use to create the signature of your method that accepts filtering and pagination parameters:
|
||||
|
||||
@@ -298,7 +297,7 @@ class PostService extends TransactionBaseService {
|
||||
}
|
||||
```
|
||||
|
||||
Then, any other resources such as endpoints or services that use this method can pass what relations to expand in the next parameter:
|
||||
Then, any other resources such as API Route or services that use this method can pass what relations to expand in the next parameter:
|
||||
|
||||
```ts
|
||||
await postService.retrieve(id, {
|
||||
@@ -314,7 +313,7 @@ When you need to throw errors in your service methods, it's recommended to use `
|
||||
|
||||
:::note
|
||||
|
||||
This assumes you're handling errors in your custom endpoints as explained [here](../endpoints/create.mdx#handle-errors).
|
||||
This assumes you're handling errors in your custom API Route as explained [here](../api-routes/create.mdx#handle-errors).
|
||||
|
||||
:::
|
||||
|
||||
@@ -378,9 +377,9 @@ class MyService extends TransactionBaseService {
|
||||
}
|
||||
```
|
||||
|
||||
### In an Endpoint
|
||||
### In an API Route
|
||||
|
||||
To use your custom service in an endpoint, you can use `req.scope.resolve` passing it the service’s registration name:
|
||||
To use your custom service in an API Route, you can use `MedusaRequest` object's `scope.resolve` method passing it the service’s registration name:
|
||||
|
||||
```ts
|
||||
const postService = req.scope.resolve("postService")
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
---
|
||||
description: 'Learn how to create a service in Medusa. This guide also includes how to use services in other services, subscribers, and endpoints.'
|
||||
addHowToData: true
|
||||
---
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ For example, you can use Medusa’s `productService` to get the list of products
|
||||
|
||||
In the Medusa backend, custom services are TypeScript or JavaScript files located in the `src/services` directory. Each service should be a class that extends the `TransactionBaseService` class from the core Medusa package `@medusajs/medusa`. Each file you create in `src/services` should hold one service and export it.
|
||||
|
||||
The file name is important as it determines the name of the service when you need to use it elsewhere. The name of the service will be registered in the dependency container as the camel-case version of the file name with `Service` appended to the end of the name. Other resources, such as other services or endpoints, will use that name when resolving the service from the dependency container.
|
||||
The file name is important as it determines the name of the service when you need to use it elsewhere. The name of the service will be registered in the dependency container as the camel-case version of the file name with `Service` appended to the end of the name. Other resources, such as other services or API Routes, will use that name when resolving the service from the dependency container.
|
||||
|
||||
For example, if the file name is `hello.ts`, the service will be registered as `helloService` in the dependency container. If the file name is `hello-world.ts`, the service name will be registered as `helloWorldService`.
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ In this document, you’ll learn how to override a strategy in a Medusa backend
|
||||
|
||||
The Medusa core defines and uses strategies for certain functionalities, which allows developers to override these functionalities and implement them as fits for their use case.
|
||||
|
||||
For example, the cart completion process is implemented within a `CartCompletionStrategy` that is used inside the Complete Cart endpoint. If you need to change the cart completion process, you can override the `CartCompletionStrategy` and implement your own strategy. The Medusa backend will then use your strategy instead of the one defined in the core.
|
||||
For example, the cart completion process is implemented within a `CartCompletionStrategy` that is used inside the Complete Cart API Route. If you need to change the cart completion process, you can override the `CartCompletionStrategy` and implement your own strategy. The Medusa backend will then use your strategy instead of the one defined in the core.
|
||||
|
||||
### Hierarchy of Strategy Resolution
|
||||
|
||||
|
||||
@@ -15,9 +15,9 @@ A strategy is an isolated piece of business logic that can be overridden and cus
|
||||
|
||||
For example, in the core `@medusajs/medusa` package, strategies are used to implement functionalities like cart completion and product import.
|
||||
|
||||
These strategy classes are then resolved in endpoints, services, or wherever needed using dependency injection and used to perform their designated functionality.
|
||||
These strategy classes are then resolved in API Routes, services, or wherever needed using dependency injection and used to perform their designated functionality.
|
||||
|
||||
For example, the `CartCompletionStrategy` is resolved in the Complete Cart endpoint that is defined in the core `@medusajs/medusa` package. It’s then used to complete the cart and place the order:
|
||||
For example, the `CartCompletionStrategy` is resolved in the Complete Cart API Route that is defined in the core `@medusajs/medusa` package. It’s then used to complete the cart and place the order:
|
||||
|
||||
```ts
|
||||
export default async (req, res) => {
|
||||
|
||||
Reference in New Issue
Block a user