Files
medusa-store/docs/content/advanced/backend/endpoints/add.md
Shahed Nasser 589cb18f98 docs: improved SEO of documentation (#3117)
* docs: added description to documentation pages

* docs: added more descriptions

* docs: finished improving meta description

* docs: added searchbox structured data

* docs: added breadcrumbs structured data

* docs: added how to structured data

* docs: improved 404 page

* docs: added how-to frontmatter option
2023-01-26 15:58:33 +02:00

307 lines
8.6 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
description: 'Learn how to create endpoints in the Medusa server. This guide also includes how to add CORS configurations, creating multiple endpoints, adding protected routes, and more.'
addHowToData: true
---
# How to Create Endpoints
In this document, youll learn how to create endpoints in your Medusa server.
## Overview
Custom endpoints reside under the `src/api` directory in your Medusa Backend. They're defined in a TypeScript or JavaScript file that is named `index` (for example, `index.ts`). This file should export a function that returns an Express router.
---
## Implementation
To create a new endpoint, start by creating a new file in `src/api` called `index.ts`. At its basic format, `index.ts` should look something like this:
```ts title=src/api/index.ts
import { Router } from "express"
export default (rootDirectory, pluginOptions) => {
const router = Router()
router.get("/hello", (req, res) => {
res.json({
message: "Welcome to My Store!",
})
})
return router
}
```
This exports a function that returns an Express router. The function receives two parameters:
- `rootDirectory` is the absolute path to the root directory that your server is running from.
- `pluginOptions` is an object that has your plugin's options. If your API route is not implemented in a plugin, then it will be an empty object.
### Endpoints Path
Your endpoint can be under any path you wish.
By Medusas conventions:
- All Storefront REST APIs are prefixed by `/store`. For example, the `/store/products` endpoint lets you retrieve the products to display them on your storefront.
- All Admin REST APIs are prefixed by `/admin`. For example, the `/admin/products` endpoint lets you retrieve the products to display them on your Admin.
You can also create endpoints that don't reside under these two prefixes, similar to the `hello` endpoint in the previous example.
---
## CORS Configuration
If youre adding a storefront or admin endpoint and you want to access these endpoints from the storefront or Medusa admin, you need to pass your endpoints Cross-Origin Resource Origin (CORS) options using the `cors` package.
First, you need to import the necessary utility functions and types from Medusa's packages with the `cors` library:
```ts
import { getConfigFile, parseCorsOrigins } from "medusa-core-utils"
import { ConfigModule } from "@medusajs/medusa/dist/types/global"
import cors from "cors"
```
Next, in the exported function, retrieve the CORS configurations of your server using the utility functions you imported:
```ts
export default (rootDirectory) => {
// ...
const { configModule } =
getConfigFile<ConfigModule>(rootDirectory, "medusa-config")
const { projectConfig } = configModule
// ....
}
```
Then, create an object that will hold the CORS configurations. If its a storefront endpoint, pass the `origin` property storefront options:
```ts
const corsOptions = {
origin: projectConfig.store_cors.split(","),
credentials: true,
}
```
If its an admin endpoint, pass the `origin` property admin options:
```ts
const corsOptions = {
origin: projectConfig.admin_cors.split(","),
credentials: true,
}
```
Finally, for each route you add, create an `OPTIONS` request and add `cors` as a middleware for the route passing it the CORS option:
```ts
router.options("/admin/hello", cors(corsOptions))
router.get("/admin/hello", cors(corsOptions), (req, res) => {
// ...
})
```
---
## Create Multiple Endpoints
### Same File
You can add more than one endpoint in `src/api/index.ts`:
```ts title=src/api/index.ts
router.options("/store/hello", cors(storeCorsOptions))
router.get("/store/hello", cors(storeCorsOptions), (req, res) => {
res.json({
message: "Welcome to Your Store!",
})
})
router.options("/admin/hello", cors(adminCorsOptions))
router.get("/admin/hello", cors(adminCorsOptions), (req, res) => {
res.json({
message: "Welcome to Your Admin!",
})
})
```
### Multiple Files
Alternatively, you can add multiple files for each endpoint or set of endpoints for readability and easy maintenance.
To do that with the previous example, first, create the file `src/api/store.ts` with the following content:
```ts title=src/api/store.ts
import cors from "cors"
import { projectConfig } from "../../medusa-config"
export default (router) => {
const storeCorsOptions = {
origin: projectConfig.store_cors.split(","),
credentials: true,
}
router.options("/store/hello", cors(storeCorsOptions))
router.get("/store/hello", cors(storeCorsOptions), (req, res) => {
res.json({
message: "Welcome to Your Store!",
})
})
}
```
You export a function that receives an Express router as a parameter and adds the endpoint `store/hello` to it.
Next, create the file `src/api/admin.ts` with the following content:
```ts title=src/api/admin.ts
import cors from "cors"
import { projectConfig } from "../../medusa-config"
export default (router) => {
const adminCorsOptions = {
origin: projectConfig.admin_cors.split(","),
credentials: true,
}
router.options("/admin/hello", cors(adminCorsOptions))
router.get("/admin/hello", cors(adminCorsOptions), (req, res) => {
res.json({
message: "Welcome to Your Admin!",
})
})
}
```
Again, you export a function that receives an Express router as a parameter and adds the endpoint `admin/hello` to it.
Finally, in `src/api/index.ts` import the two functions at the beginning of the file:
```ts title=src/api/index.ts
import { Router } from "express"
import storeRoutes from "./store"
import adminRoutes from "./admin"
```
and in the exported function, call each of the functions passing them the Express router:
```ts title=src/api/index.ts
export default () => {
const router = Router()
storeRoutes(router)
adminRoutes(router)
return router
}
```
---
## Protected Routes
Protected routes are routes that should be accessible by logged-in customers or users only.
### Protect Store Routes
To make a storefront route protected, first, import the `authenticate-customer` middleware:
```ts
import
authenticate
from "@medusajs/medusa/dist/api/middlewares/authenticate-customer"
```
Then, add the middleware to your route:
```ts
router.options("/store/hello", cors(corsOptions))
router.get("/store/hello", cors(corsOptions), authenticate(),
async (req, res) => {
if (req.user) {
// user is logged in
// to get customer id: req.user.customer_id
}
// ...
}
)
```
Please note that the endpoint is still accessible by all users, however, youll be able to access the current logged-in customer if theres any.
To disallow guest customers from accessing the endpoint, you can throw an error if `req.user` is `false`.
### Protect Admin Routes
To make an admin route protected, first, import the `authenticate` middleware:
```ts
import
authenticate
from "@medusajs/medusa/dist/api/middlewares/authenticate"
```
Then, add the middleware to your route:
```ts
router.options("/admin/products/count", cors(corsOptions))
router.get("/admin/products/count", cors(corsOptions), authenticate(),
async (req, res) => {
// access current user
const id = req.user.userId
const userService = req.scope.resolve("userService")
const user = await userService.retrieve(id)
// ...
}
)
```
Now, only authenticated users can access this endpoint.
---
## Use Services
Services in Medusa bundle a set of functionalities into one class. Then, you can use that class anywhere in your backend. For example, you can use the `ProductService` to retrieve products or perform operations like creating or updating a product.
You can retrieve any registered service in your endpoint using `req.scope.resolve` passing it the services registration name.
Heres an example of an endpoint that retrieves the count of products in your store:
```ts
router.get("/admin/products/count", cors(corsOptions), authenticate(),
(req, res) => {
const productService = req.scope.resolve("productService")
productService.count().then((count) => {
res.json({
count,
})
})
}
)
```
The `productService` has a `count` method that returns a Promise. This Promise resolves to the count of the products. You return a JSON of the product count.
---
## Building Files
Custom endpoints must be transpiled and moved to the `dist` directory. This happens when you run your server using `medusa develop` and while its running, and when you run the following command:
```bash npm2yarn
npm run build
```
---
## See Also
- [Add a Middleware](./add-middleware.md)
- [Storefront API Reference](/api/store)
- [Admin API Reference](/api/admin)
- [Create a Service](./../services/create-service.md).