chore: reorganize docs apps (#7228)
* reorganize docs apps * add README * fix directory * add condition for old docs
This commit is contained in:
@@ -0,0 +1,104 @@
|
||||
export const metadata = {
|
||||
title: `${pageNumber} Handling CORS in API Routes`,
|
||||
}
|
||||
|
||||
# {metadata.title}
|
||||
|
||||
In this chapter, you’ll learn about the CORS middleware and how to configure it for custom API routes.
|
||||
|
||||
## CORS Overview
|
||||
|
||||
Cross-Origin Resource Sharing (CORS) allows only configured origins to access your API Routes. For example, if you allow only origins starting with `http://localhost:7001` to access your Admin API Routes, other origins accessing those routes get a CORS error.
|
||||
|
||||
### CORS Configurations
|
||||
|
||||
You can configure allowed origins for Store and Admin API Routes using the `store_cors` and `admin_cors` configurations in `medusa-config.js`. Each of these configurations accepts a URL pattern to identify allowed origins.
|
||||
|
||||
For example:
|
||||
|
||||
```js title="medusa-config.js"
|
||||
module.exports = {
|
||||
projectConfig: {
|
||||
admin_cors: "http://localhost:7001",
|
||||
store_cors: "http://localhost:8000",
|
||||
// ...
|
||||
},
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
This allows the `http://localhost:7001` origin to access the Admin API Routes, and the `http://localhost:8000` origin to access Store API Routes.
|
||||
|
||||
---
|
||||
|
||||
## CORS in Store and Admin Routes
|
||||
|
||||
Medusa applies the CORS middleware with the specified configurations in `medusa-config.js` on all routes starting with `/store` and `/admin`.
|
||||
|
||||
To disable the CORS middleware for a route, export a `CORS` variable in the route file with its value set to `false`.
|
||||
|
||||
For example:
|
||||
|
||||
```ts title="src/api/store/custom/route.ts" highlights={[["15"]]}
|
||||
import type {
|
||||
MedusaRequest,
|
||||
MedusaResponse,
|
||||
} from "@medusajs/medusa"
|
||||
|
||||
export const GET = (
|
||||
req: MedusaRequest,
|
||||
res: MedusaResponse
|
||||
) => {
|
||||
res.json({
|
||||
message: "[GET] Hello world!",
|
||||
})
|
||||
}
|
||||
|
||||
export const CORS = false
|
||||
```
|
||||
|
||||
This disables the CORS middleware on API Routes at the path `/store/custom`.
|
||||
|
||||
---
|
||||
|
||||
## CORS in Custom Routes
|
||||
|
||||
If you create a route that doesn’t start with `/store` or `/admin`, you must apply the CORS middleware manually. Otherwise, all requests to your API route cause a CORS error.
|
||||
|
||||
You can do that in the exported middlewares configurations in `src/api/middlewares.ts`.
|
||||
|
||||
For example:
|
||||
|
||||
export const highlights = [["18", "parseCorsOrigins", "A utility function that parses the CORS configurations in `medusa-config.js`"]]
|
||||
|
||||
```ts title="src/api/middlewares.ts" highlights={highlights}
|
||||
import {
|
||||
ConfigModule,
|
||||
MiddlewaresConfig,
|
||||
} from "@medusajs/medusa"
|
||||
import { parseCorsOrigins } from "medusa-core-utils"
|
||||
import cors from "cors"
|
||||
|
||||
export const config: MiddlewaresConfig = {
|
||||
routes: [
|
||||
{
|
||||
matcher: "/custom*",
|
||||
middlewares: [
|
||||
(req, res, next) => {
|
||||
const configModule: ConfigModule =
|
||||
req.scope.resolve("configModule")
|
||||
|
||||
return cors({
|
||||
origin: parseCorsOrigins(
|
||||
configModule.projectConfig.store_cors
|
||||
),
|
||||
credentials: true,
|
||||
})(req, res, next)
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
This retrieves the configurations exported from `medusa-config.js` and applies the `store_cors` to routes starting with `/custom`.
|
||||
@@ -0,0 +1,47 @@
|
||||
export const metadata = {
|
||||
title: `${pageNumber} HTTP Methods`,
|
||||
}
|
||||
|
||||
# {metadata.title}
|
||||
|
||||
In this chapter, you'll learn about how to add new API routes for each HTTP method.
|
||||
|
||||
## Handlers of HTTP Methods
|
||||
|
||||
You can define a handler function for each HTTP method in a route file. The function’s name is the name of the HTTP method it handles.
|
||||
|
||||
Allowed HTTP methods are: `GET`, `POST`, `DELETE`, `PUT`, `PATCH`, `OPTIONS`, and `HEAD`.
|
||||
|
||||
Creating a route handler function for any of the above HTTP methods exposes a new API route for that method.
|
||||
|
||||
For example, create the file `src/api/store/hello-world/route.ts` with the following content:
|
||||
|
||||
```ts title="src/api/store/hello-world/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!",
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
This adds two API Routes:
|
||||
|
||||
- A `GET` route at `localhost:9000/store/hello-world`.
|
||||
- A `POST` route at `localhost:9000/store/hello-world`.
|
||||
@@ -0,0 +1,130 @@
|
||||
export const metadata = {
|
||||
title: `${pageNumber} Middlewares`,
|
||||
}
|
||||
|
||||
# {metadata.title}
|
||||
|
||||
In this chapter, you’ll learn about middlewares and how to create them.
|
||||
|
||||
## What is a Middleware?
|
||||
|
||||
A middleware is a function executed when a request is sent to an API Route.
|
||||
|
||||
---
|
||||
|
||||
## How to Create a Middleware?
|
||||
|
||||
Middlewares are defined in the special file `src/api/middleware.ts`. The file must export an object of middleware configurations.
|
||||
|
||||
For example:
|
||||
|
||||
```ts title="src/api/middleware.ts"
|
||||
import { MiddlewaresConfig } from "@medusajs/medusa"
|
||||
|
||||
export const config: MiddlewaresConfig = {
|
||||
routes: [
|
||||
{
|
||||
matcher: "/store*",
|
||||
middlewares: [
|
||||
(req, res, next) => {
|
||||
console.log("Received a request!")
|
||||
|
||||
next()
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
The middleware configurations object has the property `routes`. Its value is an array of middleware route objects, where each object is a middleware to apply to a route pattern.
|
||||
|
||||
In the example above, you define a middleware that logs the message `Received a request!` whenever a request is sent to an API route path starting with `/store`.
|
||||
|
||||
<Note>
|
||||
|
||||
The `matcher` property can be a string or a regular expression.
|
||||
|
||||
</Note>
|
||||
|
||||
### Test Middleware
|
||||
|
||||
To test the middleware, start the application:
|
||||
|
||||
```bash npm2yarn
|
||||
npm run dev
|
||||
```
|
||||
|
||||
Then, send a request to any API route starting with `/store`, such as `localhost:9000/store/products`:
|
||||
|
||||
```bash apiTesting testApiUrl="localhost:9000/store/products" testApiMethod="GET"
|
||||
curl localhost:9000/store/products
|
||||
```
|
||||
|
||||
Once you send the request, you’ll see the following message in the terminal:
|
||||
|
||||
```bash
|
||||
Received a request!
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## When to Use
|
||||
|
||||
<Note type="success" title="Use middlewares when">
|
||||
|
||||
- You want to guard API routes by a custom condition.
|
||||
- You're modifying the request body.
|
||||
- You're registering custom resources in the Medusa container.
|
||||
|
||||
</Note>
|
||||
|
||||
---
|
||||
|
||||
## Middleware Function Parameters
|
||||
|
||||
The middleware function accepts three parameters:
|
||||
|
||||
1. A request object of type `MedusaRequest`.
|
||||
2. A response object of type `MedusaResponse`.
|
||||
3. A function of type `MedusaNextFunction` that executes the next middleware in the stack.
|
||||
|
||||
<Note title="Important">
|
||||
|
||||
You must call the `next` function in the middleware. Otherwise, other middlewares and the API route handler won’t execute.
|
||||
|
||||
</Note>
|
||||
|
||||
---
|
||||
|
||||
## Restrict HTTP Methods
|
||||
|
||||
In addition to the `matcher` configuration, you can restrict which HTTP methods the middleware is applied to.
|
||||
|
||||
The object in the `routes` array accepts the property `method` whose value is either a string or an array of strings. Each string is an allowed HTTP method. If no value is specified, the middlewares are applied to requests of all HTTP methods.
|
||||
|
||||
For example:
|
||||
|
||||
export const highlights = [["7", "", "Apply the middleware only on `POST` requests"]]
|
||||
|
||||
```ts title="src/api/middlewares.ts" highlights={highlights}
|
||||
import { MiddlewaresConfig } from "@medusajs/medusa"
|
||||
|
||||
export const config: MiddlewaresConfig = {
|
||||
routes: [
|
||||
{
|
||||
matcher: "/store*",
|
||||
method: "POST",
|
||||
middlewares: [
|
||||
(req, res, next) => {
|
||||
console.log("Received a request!")
|
||||
|
||||
next()
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
This applies the middleware only when a `POST` request is sent to an API route path starting with `/store`.
|
||||
@@ -0,0 +1,146 @@
|
||||
export const metadata = {
|
||||
title: `${pageNumber} API Route Parameters`,
|
||||
}
|
||||
|
||||
# {metadata.title}
|
||||
|
||||
In this chapter, you’ll learn about path, query, and request body parameters.
|
||||
|
||||
## Path Parameters
|
||||
|
||||
To define a path parameter for an API route, create a directory as part of the route file’s path. The directory’s name is of the format `[param]`, where `param` is the name of the parameters.
|
||||
|
||||
For example, to create an API Route at the path `/message/{id}`, where `{id}` is an ID that can be passed to the route, create the file `src/api/store/hello-world/[id]/route.ts` with the following content:
|
||||
|
||||
export const singlePathHighlights = [
|
||||
["11", "req.params.id", "Access the path parameter `id`"]
|
||||
]
|
||||
|
||||
```ts title="src/api/store/hello-world/[id]/route.ts" highlights={singlePathHighlights}
|
||||
import type {
|
||||
MedusaRequest,
|
||||
MedusaResponse,
|
||||
} from "@medusajs/medusa"
|
||||
|
||||
export const GET = (
|
||||
req: MedusaRequest,
|
||||
res: MedusaResponse
|
||||
) => {
|
||||
res.json({
|
||||
message: `[GET] Hello ${req.params.id}!`,
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
You can access the path parameter using the `params` property of the `MedusaRequest` parameter. The `params` property is an object whose keys are the parameter names, and the values are each parameter’s value passed in the route’s path.
|
||||
|
||||
### Multiple Path Parameters
|
||||
|
||||
Each directory in the route file’s path whose name is of the format `[param]` is considered a path parameter. However, every parameter name must be unique.
|
||||
|
||||
For example, you can create an API route at `src/api/store/hello-world/[id]/name/[name]/route.ts`:
|
||||
|
||||
export const multiplePathHighlights = [
|
||||
["11", "req.params.id", "Access the path parameter `id`"],
|
||||
["11", "req.params.name", "Access the path parameter `name`"]
|
||||
]
|
||||
|
||||
```ts title="src/api/store/hello-world/[id]/name/[name]/route.ts" highlights={multiplePathHighlights}
|
||||
import type {
|
||||
MedusaRequest,
|
||||
MedusaResponse,
|
||||
} from "@medusajs/medusa"
|
||||
|
||||
export const GET = (
|
||||
req: MedusaRequest,
|
||||
res: MedusaResponse
|
||||
) => {
|
||||
res.json({
|
||||
message: `[GET] Hello ${req.params.id} - ${req.params.name}!`,
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
This API route expects two path parameters: `id` and `name`.
|
||||
|
||||
---
|
||||
|
||||
## Query Parameters
|
||||
|
||||
You can access all query parameters in the `query` property of the `MedusaRequest` parameter.
|
||||
|
||||
For example:
|
||||
|
||||
export const queryHighlights = [
|
||||
["11", "req.query.name", "Access the query parameter `name`"],
|
||||
]
|
||||
|
||||
```ts title="src/api/store/hello-world/route.ts" highlights={queryHighlights}
|
||||
import type {
|
||||
MedusaRequest,
|
||||
MedusaResponse,
|
||||
} from "@medusajs/medusa"
|
||||
|
||||
export async function GET(
|
||||
req: MedusaRequest,
|
||||
res: MedusaResponse
|
||||
): Promise<void> {
|
||||
res.json({
|
||||
message: `Hello ${req.query.name}`,
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Request Body Parameters
|
||||
|
||||
By default, any request sent to your Medusa application with its `Content-Type` header set to `application/json` is parsed into an object and attached to the `MedusaRequest`'s `body` property.
|
||||
|
||||
For example:
|
||||
|
||||
export const bodyHighlights = [
|
||||
["15", "req.body.name", "Access the request body parameter `name`"],
|
||||
]
|
||||
|
||||
```ts title="src/api/store/hello-world/route.ts" highlights={bodyHighlights}
|
||||
import type {
|
||||
MedusaRequest,
|
||||
MedusaResponse,
|
||||
} from "@medusajs/medusa"
|
||||
|
||||
type HelloWorldReq = {
|
||||
name: string
|
||||
}
|
||||
|
||||
export const POST = (
|
||||
req: MedusaRequest<HelloWorldReq>,
|
||||
res: MedusaResponse
|
||||
) => {
|
||||
res.json({
|
||||
message: `[POST] Hello ${req.body.name}!`,
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
The `MedusaRequest` type accepts a type argument indicating the expected request body parameters.
|
||||
|
||||
In this example, you use the `name` request body parameter to create the message in the returned response.
|
||||
|
||||
To test it out, send the following request to your Medusa application:
|
||||
|
||||
```bash apiTesting testApiUrl="http://localhost:9000/store/hello-world" testApiMethod="POST" testBodyParams={{ "name": "John" }}
|
||||
curl -X POST http://localhost:9000/store/hello-world \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data-raw '{
|
||||
"name": "John"
|
||||
}'
|
||||
```
|
||||
|
||||
This returns the following JSON object:
|
||||
|
||||
```json
|
||||
{
|
||||
"message": "[POST] Hello John!"
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,155 @@
|
||||
export const metadata = {
|
||||
title: `${pageNumber} Protected Routes`,
|
||||
}
|
||||
|
||||
# {metadata.title}
|
||||
|
||||
In this chapter, you’ll learn how to create protected routes.
|
||||
|
||||
## Default Protected Routes
|
||||
|
||||
A protected route is a route that requires requests to be user-authenticated before performing the route's functionality. Otherwise, the request fails, and the user is prevented access.
|
||||
|
||||
Medusa applies an authentication guard on the following routes:
|
||||
|
||||
- Routes starting with `/admin` require an authenticated admin user.
|
||||
- Routes starting with `/store/me` require an authenticated customer.
|
||||
|
||||
<Note>
|
||||
|
||||
Refer to the API Reference for [Admin](https://docs.medusajs.com/api/admin#authentication) and [Store](https://docs.medusajs.com/api/store#authentication) authentication methods.
|
||||
|
||||
</Note>
|
||||
|
||||
---
|
||||
|
||||
## Authentication Opt-Out
|
||||
|
||||
To disable the authentication guard on custom routes under the `/admin` or `/store/me` path prefixes, 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" highlights={[["15"]]} apiTesting testApiUrl="http://localhost:9000/store/me/custom" testApiMethod="GET"
|
||||
import type {
|
||||
MedusaRequest,
|
||||
MedusaResponse,
|
||||
} from "@medusajs/medusa"
|
||||
|
||||
export const GET = async (
|
||||
req: MedusaRequest,
|
||||
res: MedusaResponse
|
||||
) => {
|
||||
res.json({
|
||||
message: "Hello",
|
||||
})
|
||||
}
|
||||
|
||||
export const AUTHENTICATE = false
|
||||
```
|
||||
|
||||
Now, any request sent to the `/store/me/custom` API route is allowed, regardless if the customer is authenticated or not.
|
||||
|
||||
---
|
||||
|
||||
## Access Logged-In Customer
|
||||
|
||||
You can access the logged-in customer’s ID in all API routes starting with `/store` using the `user.customer_id` property of the `MedusaRequest` object.
|
||||
|
||||
For example:
|
||||
|
||||
|
||||
|
||||
```ts title="src/api/store/custom/route.ts" highlights={[["16", "", "Access the logged-in customer's ID."]]}
|
||||
import type {
|
||||
MedusaRequest,
|
||||
MedusaResponse,
|
||||
} from "@medusajs/medusa"
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
import { ICustomerModuleService } from "@medusajs/types"
|
||||
|
||||
export const GET = async (
|
||||
req: MedusaRequest,
|
||||
res: MedusaResponse
|
||||
) => {
|
||||
const customerService: ICustomerModuleService =
|
||||
req.scope.resolve(ModuleRegistrationName.CUSTOMER)
|
||||
|
||||
const customer = await customerService.retrieve(
|
||||
req.user.customer_id
|
||||
)
|
||||
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
In the route handler, you resolve the `CustomerService`, then use it to retrieve the logged-in customer, if available.
|
||||
|
||||
---
|
||||
|
||||
## Access Logged-In Admin User
|
||||
|
||||
You can access the logged-in admin user’s ID in all API Routes starting with `/admin` using the `user.userId` property of the `MedusaRequest` object.
|
||||
|
||||
For example:
|
||||
|
||||
```ts title="src/api/admin/custom/route.ts" highlights={[["16", "req.user.userId", "Access the logged-in admin user's ID."]]}
|
||||
import type {
|
||||
MedusaRequest,
|
||||
MedusaResponse,
|
||||
} from "@medusajs/medusa"
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
import { IUserModuleService } from "@medusajs/types"
|
||||
|
||||
export const GET = async (
|
||||
req: MedusaRequest,
|
||||
res: MedusaResponse
|
||||
) => {
|
||||
const userService: IUserModuleService = req.scope.resolve(
|
||||
ModuleRegistrationName.USER
|
||||
)
|
||||
|
||||
const user = await userService.retrieve(req.user.userId)
|
||||
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
In the route handler, you resolve the `UserService`, and then use it to retrieve the logged-in admin user.
|
||||
|
||||
---
|
||||
|
||||
## Protect Custom API Routes
|
||||
|
||||
To protect custom API Routes that don’t start with `/store/me` or `/admin`, apply one of the following middlewares exported by the `@medusajs/medusa` package on your routes:
|
||||
|
||||
- `authenticate`: only authenticated admin users can access the API Route. You can access the user's ID in the API Route method handler using the `MedusaRequest` object's `user.userId`.
|
||||
- `authenticateCustomer`: customer authentication isn’t required, but if a customer is logged in, it attaches their ID to the `MedusaRequest` object's `user.customer_id`.
|
||||
- `requireCustomerAuthentication`: only authenticated customers can access the 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:
|
||||
|
||||
export const highlights = [
|
||||
["11", "authenticate", "Only authenticated admin users can access routes starting with `/custom/admin`"],
|
||||
["15", "requireCustomerAuthentication", "Only authenticated customers can access routes starting with `/custom/customers`"]
|
||||
]
|
||||
|
||||
```ts title="src/api/middlewares.ts" highlights={highlights}
|
||||
import {
|
||||
authenticate,
|
||||
requireCustomerAuthentication,
|
||||
type MiddlewaresConfig,
|
||||
} from "@medusajs/medusa"
|
||||
|
||||
export const config: MiddlewaresConfig = {
|
||||
routes: [
|
||||
{
|
||||
matcher: "/custom/admin*",
|
||||
middlewares: [authenticate()],
|
||||
},
|
||||
{
|
||||
matcher: "/custom/customer*",
|
||||
middlewares: [requireCustomerAuthentication()],
|
||||
},
|
||||
],
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,66 @@
|
||||
export const metadata = {
|
||||
title: `${pageNumber} Request Body Parsers`,
|
||||
}
|
||||
|
||||
# {metadata.title}
|
||||
|
||||
In this chapter, you’ll learn how to configure request-body parsing and add new parsers.
|
||||
|
||||
## Add a Request-Body Parser
|
||||
|
||||
You can parse request bodies of other content types by adding the parser as a middleware to the routes.
|
||||
|
||||
For example:
|
||||
|
||||
export const parserHighlights = [
|
||||
["13", "", "Add a parser for the `application/x-www-form-urlencoded` content type."]
|
||||
]
|
||||
|
||||
```ts title="src/api/middlewares.ts" highlights={parserHighlights}
|
||||
import type {
|
||||
MiddlewaresConfig,
|
||||
} from "@medusajs/medusa"
|
||||
import {
|
||||
urlencoded,
|
||||
} from "body-parser"
|
||||
|
||||
export const config: MiddlewaresConfig = {
|
||||
routes: [
|
||||
{
|
||||
matcher: "*",
|
||||
middlewares: [
|
||||
urlencoded({ extended: true }),
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
This adds a parser for the `application/x-www-form-urlencoded` content type and attaches the parsed data to the `MedusaRequest` object’s `body` property.
|
||||
|
||||
---
|
||||
|
||||
## Parse Webhook Body Parameters
|
||||
|
||||
Webhook API Routes may require the `raw` body parser middleware rather than the default `json`.
|
||||
|
||||
To change the default parser, set the `bodyParser` property of a middleware route object to `false`, and pass the preferred body-parser middleware in the `middlewares` property.
|
||||
|
||||
For example:
|
||||
|
||||
```ts title="src/api/middlewares.ts" highlights={[["8", "", "Disables the default request-body parser."], ["9", "raw", "Add a new body parser."]]}
|
||||
import { MiddlewaresConfig } from "@medusajs/medusa"
|
||||
import { raw } from "body-parser"
|
||||
|
||||
export const config: MiddlewaresConfig = {
|
||||
routes: [
|
||||
{
|
||||
matcher: "/webhooks*",
|
||||
bodyParser: false,
|
||||
middlewares: [raw({ type: "application/json" })],
|
||||
},
|
||||
],
|
||||
}
|
||||
```
|
||||
|
||||
This changes the request-body parser to use the `raw` middleware on routes starting with `/webhooks`.
|
||||
Reference in New Issue
Block a user