Skip to main content

How to Create Endpoints

In this document, you’ll learn how to create endpoints in Medusa.

Overview

Custom endpoints are created under the src/apiCopy to Clipboard directory in your Medusa Backend. They're defined in a TypeScript or JavaScript file named indexCopy to Clipboard (for example, index.tsCopy to Clipboard). This file should export a function that returns an Express router

They're then transpiled into the /dist/apiCopy to Clipboard directory to be consumed.


Implementation

To create a new endpoint, start by creating a new file in src/apiCopy to Clipboard called index.tsCopy to Clipboard. At its basic format, index.tsCopy to Clipboard should look something like this:

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
}
Report Incorrect CodeCopy to Clipboard

This exports a function that returns an Express router. The function receives two parameters:

  • rootDirectoryCopy to Clipboard is the absolute path to the root directory that your backend is running from.
  • pluginOptionsCopy to Clipboard 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 Medusa’s conventions:

  • All Storefront REST APIs are prefixed by /storeCopy to Clipboard. For example, the /store/productsCopy to Clipboard endpoint lets you retrieve the products to display them on your storefront.
  • All Admin REST APIs are prefixed by /adminCopy to Clipboard. For example, the /admin/productsCopy to Clipboard 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 helloCopy to Clipboard endpoint in the previous example.


CORS Configuration

If you’re 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 corsCopy to Clipboard package.

First, you need to import the necessary utility functions and types from Medusa's packages with the corsCopy to Clipboard library:

import { 
getConfigFile,
parseCorsOrigins,
} from "medusa-core-utils"
import {
ConfigModule,
} from "@medusajs/medusa/dist/types/global"
import cors from "cors"
Report Incorrect CodeCopy to Clipboard

Next, in the exported function, retrieve the CORS configurations of your backend using the utility functions you imported:

export default (rootDirectory) => {
// ...

const { configModule } =
getConfigFile<ConfigModule>(rootDirectory, "medusa-config")
const { projectConfig } = configModule

// ....
}
Report Incorrect CodeCopy to Clipboard

Then, create an object that will hold the CORS configurations. If it’s a storefront endpoint, pass the originCopy to Clipboard property storefront options:

const corsOptions = {
origin: projectConfig.store_cors.split(","),
credentials: true,
}
Report Incorrect CodeCopy to Clipboard

If it’s an admin endpoint, pass the originCopy to Clipboard property admin options:

const corsOptions = {
origin: projectConfig.admin_cors.split(","),
credentials: true,
}
Report Incorrect CodeCopy to Clipboard

Finally, for each route you add, create an OPTIONSCopy to Clipboard request and add corsCopy to Clipboard as a middleware for the route passing it the CORS option:

router.options("/admin/hello", cors(corsOptions))
router.get("/admin/hello", cors(corsOptions), (req, res) => {
// ...
})
Report Incorrect CodeCopy to Clipboard

Create Multiple Endpoints

Same File

You can add more than one endpoint in src/api/index.tsCopy to Clipboard:

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!",
})
}
)
Report Incorrect CodeCopy to Clipboard

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.tsCopy to Clipboard with the following content:

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!",
})
}
)
}
Report Incorrect CodeCopy to Clipboard

You export a function that receives an Express router as a parameter and adds the endpoint store/helloCopy to Clipboard to it.

Next, create the file src/api/admin.tsCopy to Clipboard with the following content:

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!",
})
}
)
}
Report Incorrect CodeCopy to Clipboard

Again, you export a function that receives an Express router as a parameter and adds the endpoint admin/helloCopy to Clipboard to it.

Finally, in src/api/index.tsCopy to Clipboard import the two functions at the beginning of the file:

src/api/index.ts
import { Router } from "express"
import storeRoutes from "./store"
import adminRoutes from "./admin"
Report Incorrect CodeCopy to Clipboard

and in the exported function, call each of the functions passing them the Express router:

src/api/index.ts
export default () => {
const router = Router()

storeRoutes(router)
adminRoutes(router)

return router
}
Report Incorrect CodeCopy to Clipboard

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-customerCopy to Clipboard middleware:

import 
authenticate
from "@medusajs/medusa/dist/api/middlewares/authenticate-customer"
Report Incorrect CodeCopy to Clipboard

Then, add the middleware to your route:

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
}
// ...
}
)
Report Incorrect CodeCopy to Clipboard

Please note that the endpoint is still accessible by all users, however, you’ll be able to access the current logged-in customer if there’s any.

To disallow guest customers from accessing the endpoint, you can throw an error if req.userCopy to Clipboard is falseCopy to Clipboard.

Protect Admin Routes

To make an admin route protected, first, import the authenticateCopy to Clipboard middleware:

import 
authenticate
from "@medusajs/medusa/dist/api/middlewares/authenticate"
Report Incorrect CodeCopy to Clipboard

Then, add the middleware to your route:

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)
// ...
}
)
Report Incorrect CodeCopy to Clipboard

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 ProductServiceCopy to Clipboard to retrieve products or perform operations like creating or updating a product.

You can retrieve any registered service in your endpoint using req.scope.resolveCopy to Clipboard passing it the service’s registration name.

Here’s an example of an endpoint that retrieves the count of products in your store:

router.get(
"/admin/products/count",
cors(corsOptions),
authenticate(),
(req, res) => {
const productService = req.scope.resolve("productService")

productService.count().then((count) => {
res.json({
count,
})
})
}
)
Report Incorrect CodeCopy to Clipboard

The productServiceCopy to Clipboard has a countCopy to Clipboard 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 distCopy to Clipboard directory before you can start consuming them. When you run your backend using the medusa developCopy to Clipboard command, it watches the files under srcCopy to Clipboard for any changes, then triggers the buildCopy to Clipboard command and restarts the server.

The build isn't triggerd though when the backend first starts running. So, make sure to run the buildCopy to Clipboard command before starting the backend:

yarn run build
Report Incorrect CodeCopy to Clipboard

See Also