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:
Shahed Nasser
2023-10-19 15:56:26 +00:00
committed by GitHub
parent b38f73726d
commit c28935b4e8
170 changed files with 3658 additions and 3344 deletions
@@ -333,13 +333,13 @@ This will reflect the entity changes in your database.
## Create Guard Middleware
To ensure that users who have the privilege can access an endpoint, you must create a middleware that guards admin routes. This middleware will run on all authenticated admin requests to ensure that only allowed users can access an endpoint.
To ensure that users who have the privilege can access an API Route, you must create a middleware that guards admin routes. This middleware will run on all authenticated admin requests to ensure that only allowed users can access an API Route.
Since the Medusa backend uses Express, you can create a middleware and attach it to all admin routes.
<DocCard item={{
type: 'link',
href: '/development/endpoints/add-middleware',
href: '/development/api-routes/add-middleware',
label: 'Create a Middleware',
customProps: {
icon: Icons['academic-cap-solid'],
@@ -350,18 +350,26 @@ Since the Medusa backend uses Express, you can create a middleware and attach it
<details>
<summary>Example Implementation</summary>
In this example, youll create a middleware that runs on all admin-authenticated routes and checks the logged-in users permissions before giving them access to an endpoint.
In this example, youll create a middleware that runs on all admin-authenticated routes and checks the logged-in users permissions before giving them access to an API Route.
Create the file `src/api/middlewares/permission.ts` with the following content:
Create the file `src/api/middlewares.ts` with the following content:
```ts title=src/api/middlewares/permission.ts
import { UserService } from "@medusajs/medusa"
import { NextFunction, Request, Response } from "express"
```ts title=src/api/middlewares.ts
import type {
MiddlewaresConfig,
UserService,
} from "@medusajs/medusa"
import express from "express"
import type {
MedusaRequest,
MedusaResponse,
MedusaNextFunction,
} from "@medusajs/medusa"
export default async (
req: Request,
res: Response,
next: NextFunction
export const permissions = async (
req: MedusaRequest,
res: MedusaResponse,
next: MedusaNextFunction
) => {
if (!req.user || !req.user.userId) {
next()
@@ -406,48 +414,38 @@ export default async (
// deny access
res.sendStatus(401)
}
export const config: MiddlewaresConfig = {
routes: [
{
matcher: "/admin/*",
middlewares: [permissions],
},
],
}
```
In this middleware, you ensure that there is a logged-in user and the logged-in user has a role. If not, the user is admitted to access the endpoint. Here, you presume that logged-in users who dont have a role are “super-admin” users who can access all endpoints. You may choose to implement this differently.
In this middleware, you ensure that there is a logged-in user and the logged-in user has a role. If not, the user is admitted to access the API Route. Here, you presume that logged-in users who dont have a role are “super-admin” users who can access all API Routes. You may choose to implement this differently.
If theres a logged-in user that has a role, you check that the roles permissions give them access to the current endpoint. You do that by checking if a permissions metadata has a key with the same requests path. It may be better here to check for matching using regular expressions, for example, to check routes with path parameters.
If theres a logged-in user that has a role, you check that the roles permissions give them access to the current API Route. You do that by checking if a permissions metadata has a key with the same requests path. It may be better here to check for matching using regular expressions, for example, to check routes with path parameters.
Otherwise, if the users role doesnt provide them with enough permissions, you return a `401` response code.
:::tip
Notice that you use `req.path` here to get the current endpoint path. However, in middlewares, this doesnt include the mount point which is `/admin`. So, for example, if the endpoint path is `/admin/products`, `req.path` will be `/products`. You can alternatively use `req.originalUrl`. Learn more in [Expresss documentation](https://expressjs.com/en/api.html#req.originalUrl).
Notice that you use `req.path` here to get the current API Route path. However, in middlewares, this doesnt include the mount point which is `/admin`. So, for example, if the API Route path is `/admin/products`, `req.path` will be `/products`. You can alternatively use `req.originalUrl`. Learn more in [Expresss documentation](https://expressjs.com/en/api.html#req.originalUrl).
:::
Next, to ensure that this middleware is used, import it in `src/api/index.ts` and apply it on admin routes:
```ts title=src/api/index.ts
import permissionMiddleware from "./middlewares/permission"
export default (rootDirectory: string): Router | Router[] => {
// ...
const router = Router()
// ...
// use middleware on admin routes
router.use("/admin", permissionMiddleware)
return router
}
```
This assumes you already have a router with all necessary CORS configurations and body parsing middlewares. If not, you can refer to the [Create Endpoint documentation](../../../development/endpoints/create.mdx) for more details.
Make sure to use the permission middleware after all router configurations if you want the middleware to work on your custom admin routes.
After defining the middleware, you apply it on all API Routes starting with `/admin/`.
</details>
---
## Create Endpoints and Services
## Create API Routes and Services
To manage the roles and permissions, youll need to create custom endpoints, typically for Create, Read, Update, and Delete (CRUD) operations.
To manage the roles and permissions, youll need to create custom API Routes, typically for Create, Read, Update, and Delete (CRUD) operations.
Youll also need to create a service for each of `Role` and `Permission` entities to perform these operations on them. The entity uses the service within its code.
@@ -456,11 +454,11 @@ Furthermore, you may need to extend core services if you need to perform actions
<DocCardList colSize={4} items={[
{
type: 'link',
href: '/development/endpoints/create',
label: 'Create Endpoint',
href: '/development/api-routes/create',
label: 'Create API Route',
customProps: {
icon: Icons['academic-cap-solid'],
description: 'Learn how to create an endpoint in Medusa.',
description: 'Learn how to create an API Route in Medusa.',
}
},
{
@@ -488,9 +486,9 @@ Furthermore, you may need to extend core services if you need to perform actions
Example Implementation
</summary>
In this example, youll only implement two endpoints for simplicity: create role endpoint that create a new role with permissions, and associate user endpoint that associates a user with a role.
In this example, youll only implement two API Routes for simplicity: create role API Route that create a new role with permissions, and associate user API Route that associates a user with a role.
Youll also create basic services for `Role` and `Permission` to perform the functionalities of each of these endpoints and extend the core `UserService` to allow associating roles with users.
Youll also create basic services for `Role` and `Permission` to perform the functionalities of each of these API Routes and extend the core `UserService` to allow associating roles with users.
Start by creating the file `src/services/permission.ts` with the following content:
@@ -663,15 +661,21 @@ This creates the `RoleService` with three methods:
- `create`: Creates a new role and, if provided, its permissions as well.
- `associateUser`: associates a user with a role.
Now, you can create the endpoints.
Now, you can create the API Routes.
Start by creating the file `src/api/routes/admin/role/create-role.ts` with the following content:
Start by creating the file `src/api/admin/roles/route.ts` with the following content:
```ts title=src/api/routes/admin/role/create-role.ts
import { Request, Response } from "express"
```ts title=src/api/admin/roles/route.ts
import type {
MedusaRequest,
MedusaResponse,
} from "@medusajs/medusa"
import RoleService from "../../../../services/role"
export default async (req: Request, res: Response) => {
export const POST = async (
req: MedusaRequest,
res: MedusaResponse
) => {
// omitting validation for simplicity
const {
name,
@@ -693,15 +697,21 @@ export default async (req: Request, res: Response) => {
}
```
This creates the Create Role endpoint that uses the `RoleService` to create a new role. Notice that validation of received body parameters is omitted for simplicity.
This creates the Create Role API Route at `/admin/roles` that uses the `RoleService` to create a new role. Notice that validation of received body parameters is omitted for simplicity.
Next, create the file `src/api/routes/admin/role/associate-user.ts` with the following content:
Next, create the file `src/api/routes/admin/roles/[id]/user/[user_id]/route.ts` with the following content:
```ts title=src/api/routes/admin/role/associate-user.ts
import { Request, Response } from "express"
```ts title=src/api/routes/admin/roles/[id]/user/[user_id]/route.ts
import type {
MedusaRequest,
MedusaResponse,
} from "@medusajs/medusa"
import RoleService from "../../../../services/role"
export default async (req: Request, res: Response) => {
export const POST = async (
req: MedusaRequest,
res: MedusaResponse
) => {
// omitting validation for simplicity purposes
const {
id,
@@ -717,44 +727,9 @@ export default async (req: Request, res: Response) => {
}
```
This creates the Associate User endpoint that uses the `RoleService` to associate a role with a user.
This creates the Associate User API Route at `/admin/roles/[id]/user/[user_id]` that uses the `RoleService` to associate a role with a user.
You now have to register and export these endpoints.
To do that, create the file `src/api/routes/admin/role/index.ts` with the following content:
```ts title=src/api/routes/admin/role/index.ts
import { wrapHandler } from "@medusajs/utils"
import { Router } from "express"
import createRole from "./create-role"
import associateUser from "./associate-user"
const router = Router()
export default (adminRouter: Router) => {
adminRouter.use("/roles", router)
router.post("/", wrapHandler(createRole))
router.post("/:id/user/:user_id", wrapHandler(associateUser))
}
```
This adds the create role endpoint under the path `/admin/roles`, and the associate user endpoint under the path `/admin/roles/:id/user/:user_id`, where `:id` is the ID of the role and `:user_id` is the ID of the user to associate with the role.
Finally, you can either export these routes in `src/api/routes/admin/index.ts` or, if the file is not available in your project, in `src/api/index.ts`:
```ts title=src/api/routes/admin/index.ts
import roleRouter from "./role"
const router = Router()
export function attachAdminRoutes(adminRouter: Router) {
roleRouter(adminRouter)
// ....
}
```
To test it out, run the `build` command in the root directory of your Medusa backend project:
To test the API Routes out, run the `build` command in the root directory of your Medusa backend project:
```bash npm2yarn
npm run build
@@ -766,30 +741,30 @@ Then, start the backend with the following command:
npx medusa develop
```
Try first to log in using the [Admin User Login endpoint](https://docs.medusajs.com/api/admin#auth_postauth) with an existing admin user. Then, send a `POST` request to the `localhost:9000/admin/roles` endpoint with the following request body parameters:
Try first to log in using the [Admin User Login API Route](https://docs.medusajs.com/api/admin#auth_postauth) with an existing admin user. Then, send a `POST` request to the `localhost:9000/admin/roles` API Route with the following request body parameters:
```json
{
"store_id": "store_01H8XPDY8WA1Z650MZSEY4Y0V0",
"name": "Product Manager",
"permissions": [
{
"name": "Allow Products",
"metadata": {
"/products": true
}
}
]
"store_id": "store_01H8XPDY8WA1Z650MZSEY4Y0V0",
"name": "Product Manager",
"permissions": [
{
"name": "Allow Products",
"metadata": {
"/products": true
}
}
]
}
```
Make sure to replace the `store_id`'s value with your stores ID. You can retrieve the stores ID using the [Get Store Details endpoint](https://docs.medusajs.com/api/admin#store_getstore).
Make sure to replace the `store_id`'s value with your stores ID. You can retrieve the stores ID using the [Get Store Details API Route](https://docs.medusajs.com/api/admin#store_getstore).
This will create a new role with a permission that allows users of this role to access the `/admin/products` endpoint. As mentioned before, because of the middlewares implementation, you must specify the path without the `/admin` prefix. If you chose to implement this differently, such as with regular expressions, then change the permissions metadata accordingly.
This will create a new role with a permission that allows users of this role to access the `/admin/products` API Route. As mentioned before, because of the middlewares implementation, you must specify the path without the `/admin` prefix. If you chose to implement this differently, such as with regular expressions, then change the permissions metadata accordingly.
Next, create a new user using the [Create User endpoint](https://docs.medusajs.com/api/admin#users_postusers). Then, send a `POST` request to `localhost:9000/admin/roles/<role_id>/user/<user_id>`, where `<role_id>` is the ID of the role you created, and `<user_id>` is the ID of the user you created. This will associate the user with the role you created.
Next, create a new user using the [Create User API Route](https://docs.medusajs.com/api/admin#users_postusers). Then, send a `POST` request to `localhost:9000/admin/roles/<role_id>/user/<user_id>`, where `<role_id>` is the ID of the role you created, and `<user_id>` is the ID of the user you created. This will associate the user with the role you created.
Finally, login with the user you created, then try to access any endpoint other than `/admin/products`. Youll receive a `401` unauthorized response. Then, try to access the [List Products endpoint](https://docs.medusajs.com/api/admin#products_getproducts), and the user should be able to access it as expected.
Finally, login with the user you created, then try to access any API Route other than `/admin/products`. Youll receive a `401` unauthorized response. Then, try to access the [List Products API Route](https://docs.medusajs.com/api/admin#products_getproducts), and the user should be able to access it as expected.
</details>