B2B Commerce in Node.js set up a backend with Medusa (1/2)

B2B Commerce in Node.js set up a backend with Medusa (1/2)

This tutorial series covers how you can use Medusa and its different components and features to implement a B2B commerce store. The series is split in two parts:

  • [This article] Part one: Covers how to prepare your backend to handle B2B shopping experiences

  • [Link] Part two: Covers how to customize our Next.js storefront to create a B2B storefront.

You can check out the full code for this tutorial series in this GitHub repository.

Why use open source for B2B commerce?

B2B commerce has seen rapid growth in recent years and is now among the top revenue-generating channels for B2B businesses according to a recent McKinsey study.

Yet, many businesses struggle to get their B2B setup right. Mostly, challenges come from a need to build more custom logic and integration to handle the higher complexity of B2B sales.

Some examples of B2B complexities:

  • Unique customers: Special purchasing agreements require the possibility to create custom logic for individual B2B customer groups

  • Special pricing: The commerce engine needs to be able to differentiate pricing and discounts based on customer groups

  • Omnichannel need: Selling to B2B is often not a one-channel-effort and the commerce engine needs to be able to handle multiple channels

Why use Medusa

Medusa is an open source composable commerce engine. Unlike existing proprietary platforms, its value proposition comes from the flexible and open architecture that equips tech teams with a unique possibility to build bespoke commerce setups. This is even more important in the context of complex B2B businesses

Medusa provides the foundational commerce infrastructure for businesses to take full ownership of their tech stack and avoid compromising on business needs. Custom logic can be injected natively instead of through hacky workarounds.

Our developer-first approach is a central element in this promise of customization and composability. That is why we want to give developers the essential features we find necessary for building a B2B commerce setup.

In this guide, we’ll take you through how you combine these to achieve a scaleable B2B setup.

Medusa’s Recipe for a B2B Commerce Store

Medusa provides many essential commerce features. For the B2B case, in particular, you’ll be using the following features in Medusa to create a B2B commerce store:

  • Sales Channels: Sales Channels allow you to segment your product catalog across the channels you sell through. For B2B commerce stores, Sales Channels can be used to specify which products are available for B2B customers.

  • Customer Groups: Customer Groups allow you to combine customers with similar attributes. For B2B commerce stores, they can be used to combine customers that belong to an organization that purchases products from your store.

  • Price Lists: Price Lists can be used to override products’ prices based on different conditions. For B2B commerce stores, they can be used to set the prices negotiated with B2B customers.

Prerequisites

Node.js

Medusa is a Node.js commerce platform. So, you must have at least version 14 of Node.js installed on your machine.

PostgreSQL

Although Medusa works with an SQLite database, it is highly recommended to use PostgreSQL instead. You can follow our documentation to learn how to install it.

Make sure to also create an empty PostgreSQL database.

Medusa CLI Tool

You’ll need Medusa’s CLI tool to install a Medusa server. You can install the CLI tool with the following command:

npm install @medusajs/medusa-cli -g

If you face any error with this installation, please check out the troubleshooting guide.

Install the Medusa Server

In your terminal, run the following command to install the Medusa server:

medusa new b2b-server

This will install a Medusa server under a newly created b2b-server directory. Once the command is done executing, change to that directory:

cd b2b-server

Configure Medusa Server

Enable Sales Channels

Sales Channels is a feature in Medusa that allows you to separate between the different sales channels you sell your products in, and ensure different products are available for each sales channel. For example, you can have a Sales Channel for mobile stores, another for website storefronts, and so on.

In the case of B2B stores, you can create a B2B Sales Channel that groups all wholesale products that should only be available to B2B customers.

Sales Channels is currently in beta mode, so you have to enable it manually to use it.

In the .env file of your Medusa server, add the following environment variable:

MEDUSA_FF_SALES_CHANNELS=true

This will enable sales channels on your Medusa server.

Add Database Configuration

By default, Medusa runs with an SQLite database. This is great for development purposes, but for an optimal experience, it’s recommended to use a PostgreSQL database instead.

In .env, add the following environment variable:

DATABASE_URL=<DATABASE_URL>

Where <DATABASE_URL> is the URL of your database. If you’re unsure of what your URL is, check out PostgreSQL’s documentation to see its format.

Then, in medusa-config.js, you’ll find an exported object similar to this:

module.exports = {
  projectConfig: {
    // redis_url: REDIS_URL,
    // For more production-like environment install PostgresQL
    // database_url: DATABASE_URL,
    // database_type: "postgres",
    database_database: "./medusa-db.sql",
    database_type: "sqlite",
    store_cors: STORE_CORS,
    admin_cors: ADMIN_CORS,
  },
  plugins,
};

Remove the SQLite database configuration and un-comment the PostgreSQL configurations as such:

module.exports = {
  projectConfig: {
    // redis_url: REDIS_URL,
    database_url: DATABASE_URL,
    database_type: "postgres",
    store_cors: STORE_CORS,
    admin_cors: ADMIN_CORS,
  },
  plugins,
};

Run Migrations

The Medusa server has migrations defined for all its entities so that you can smoothly prepare your database schema for your commerce store.

Run the following command in the root directory of your Medusa server to migrate entities into your database schema:

medusa migrations run

Seed Database

This step is optional but recommended for the sake of the tutorial. You can seed your database with demo data such as an admin user and products.

Run the following command in the root directory of the Medusa server to seed your database:

npm run seed

Install a File Service Plugin

File service plugins in Medusa are used to store files such as product images. Although this step is optional for this tutorial, if you want to add products with images in your store, you must install a file service plugin.

Medusa provides three official file service plugins: MinIO, Spaces, and S3. You can follow along with the documentation of the file service plugin of your choice to learn how to install it.

Test Medusa Server

In the root directory of your Medusa server, run the following command to run your Medusa server:

npm start

This runs your Medusa server on port 9000. You can test it out by opening localhost:9000/store/products in your browser. If you seeded the database earlier, you should see many products in the response. Otherwise, you can see an empty array of products in the response.

It’s recommended to keep the Medusa server running throughout the tutorial as you’ll need it for all the steps.

Install Medusa Admin

Medusa provides an admin panel that allows you to manage the different settings and data of your Medusa server, including products, sales channels, customers, and much more.

In a different directory than the Medusa server, run the following command to clone the Admin panel from GitHub:

git clone <https://github.com/medusajs/admin> b2b-admin

This creates a new directory b2b-admin and clones the codebase of the Medusa admin into it.

Change into the new directory and install the dependencies with NPM:

cd b2b-admin && npm install

Test Medusa Admin

Once the NPM installation is done, make sure that the Medusa server is running, then run the following command in the b2b-admin directory:

npm start

This runs the Medusa admin on localhost:7000 by default. If you open it in your browser, you should see a login screen.

If you seeded the database earlier, you can log in with the email admin@medusa-test.com and password supersecret. If not, you can create a user using the Medusa CLI tool.

Add Wholesale Products

After you log in to the admin, go to the Products page from the sidebar. If you’ve seeded the database you should find some products already added.

To add new products:

  1. Go to the Products page.

  2. Click on the New Product button at the top right.

  3. Enter details related to general information, variants, prices, and more.

  4. Once you’re done, click on the Publish Products button.

You can add as many products as you want.

Create B2B Sales Channel

As mentioned earlier, you’ll create a B2B Sales Channel to add Wholesale Products. These products will then only be available in that sales channel.

To create a B2B Sales Channel:

  1. Go to Settings → Sales Channels

  2. In the Sales Channels section on the left, click on the plus icon next to the title of the section.

  3. The form to create a new sales channel will open. Enter a name and description for the sales channel. You can set the name to “B2B”

  4. Once you’re done, click on the Publish Channel button.

Add Products to the Sales Channel

Now, you can add products to your B2B sales channel. To do that:

  1. Go to Settings → Sales Channels

  2. In the sales channels list on the left, choose the B2B sales channel.

  3. At the top right of the right section, click on the three dots icon.

  4. Choose from the dropdown list “Add products”.

  5. Select the products you want to add to the sales channel.

  6. Once you’re done, click on the Save button.

Create B2B Customer Groups

Customer groups allow you to group together customers that have common criteria. You can then use this group to apply special pricing for these customers.

In the B2B case in this tutorial, you’ll create a customer group for every organization that will be a B2B customer.

To differentiate every B2B customer group from other customer groups, you’ll also add in the metadata field of the customer group an is_b2b flag.

To create a B2B customer group in the admin:

  1. Go to the Customers page.

  2. Choose the Groups headline.

  3. Click on the New Group button.

  4. In the new window that opens:

    1. Enter the name of the organization in the Title field.

    2. Click on the Add Metadata button. This shows additional Key and Value fields.

    3. Enter in the Key field is_b2b and in the Value field true.

  5. Once you’re done, click on the Publish Group button.

Do these steps for every organization you want to add.

Create Customers

Before you can add customers to the customer group, you need to create the customers. You’ll need to use the API Endpoints to do that. You can use a tool like Postman to send requests, or you can use cURL.

Start by logging in using the User Login endpoint. Send a POST request to localhost:9000/admin/auth with the following request body:

 {
    "email": "admin@medusa-test.com",
    "password": "supersecret"
}

You can replace the email and password with your own if you’re using different ones.

When you log, the Session ID is set in the cookies. In tools like Postman, you can go ahead and send the rest of the requests and it appends that Session ID to the cookies of those requests. If you’re using cURL, you can learn how to set the Cookie Session ID in our documentation.

Next, you’ll create a customer using the Create a Customer endpoint. Send a POST request to localhost:9000/admin/customers with the following request body:

{
    "email": "customer@acme.com",
    "first_name": "Acme",
    "last_name": "Customer",
    "password": "supersecret"
}

You can change the values of any of the fields as necessary.

You can add as many customers as you want. Once you’re done, if you go back to the Medusa admin, you can see the new customer on the Customers page.

Add Customers to Customer Group

In this section, you’ll add the customers you created to the customer group of their organization.

To add customers to a customer group:

  1. Go to each of the B2B customer group’s details page.

  2. Click on the “Edit customers” button at the top right of the Customers section.

  3. Check the box next to every customer you want to add to the group.

  4. Click on the Save button.

Create B2B Price List

Price lists in Medusa are a list of prices that are applied on specific products under defined conditions. For example, you can set different prices for different customer groups. You can also specify prices based on the quantity of the product in the cart.

In this section, you’ll create a B2B Price List that applies to all the B2B customer groups you created in the previous sections. You’ll then set different prices for each of the wholesale products.

To create a price list:

  1. Go to the Pricing page.

  2. Click on the “Add price list” button at the top right.

  3. In the new form that opens:

    1. Choose Override under the Price list type.

    2. Expand the General collapsible section. Enter a name for the price list (for example, B2B) and a description.

    3. Expand the Configuration collapsible section. Then, toggle the Customer availability field and choose all the B2B customer groups you created.

    4. You can add prices manually at this point, you can add them manually after creating the price list, or you can import prices from a CSV file after creation. To add prices manually now:

      1. Expand the Prices collapsible section.

      2. Click on the Add Products Manually button.

      3. Check all the products you want to add then click the Save button.

      4. For all of the products you added, click on the + icon. This will expand the list of variants for that product. You can specify the same prices for all variants, remove some of the variants from the price list, and specify different prices for all variants. To specify prices:

        1. Click on the three dots icon next to each of the variants and choose Edit prices.

        2. In the new window, either select Apply overrides on selected variants or Apply on all variants and add the price for every currency in your store.

        3. Once you’re done, click Save and Close.

    5. Once you’re done, click on the Publish price list button at the top right.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1670851365484/2KqJtTe-l.png align="left")

### Importing Prices

As mentioned earlier, you can import prices from a CSV file. This will replace any prices you’ve added before.

To import prices from a CSV file:

1.  Go to the B2B price list’s details page.

2.  In the Prices section, click on the three dots icon and click on “Import price list” from the dropdown.

3.  In the new window:

    1.  If you’re unsure of what the CSV file’s format must be like, you can download a template CSV file by clicking the download icon.

    2.  To upload the CSV file you want to import prices from, either:

        1.  Drag and drop the file into the dashed box;

        2.  Or click on the dashed box and choose the file you want to import the prices from.

    3.  After you upload the CSV file, you can check the number of prices to be added above the uploaded file’s name.

4.  Once you’re done choosing a file to import prices from, click on the Import List button.


### Adding Prices Using the APIs

You can also add prices using the API endpoints. These provide you with even more options than what is currently available in the admin, such as prices based on quantity in the cart.

To add prices to a price list, send a `POST` request to `{{BACKEND_URL}}/admin/price-lists/{price_list_id}/prices/batch` (replacing `{price_list_id}` with the ID of the price list) with the following request body:

```json
{
    "prices": [
        {
            "amount": 100,
            "variant_id": "{variant_id_1}",
            "currency_code": "eur",
            "min_quantity": 10
        },
                {
            "amount": 200,
            "variant_id": "{variant_id_2}",
            "currency_code": "eur",
            "max_quantity": 9
        }
    ]
}
```

Each object in the `prices` array is an object that contains the amount, the ID of the variant to apply the price to, the currency code, and other conditions such as `min_quantity` and `max_quantity`. You can check all the conditions you can pass by referring to the [API Reference](https://docs.medusajs.com/api/admin/#tag/Price-List/operation/PostPriceListsPriceListPricesBatch).

## Add Endpoint to Check Customer Group

The last step is to create an endpoint on the Medusa server to allow you to check whether a customer belongs to a B2B customer group or not. This is helpful when you start customizing the storefront, as you need to figure out what type of customer is logged in.

To create an endpoint, create the file `b2b-server/src/api/index.ts` with the following content:

```tsx
import { CustomerService } from "@medusajs/medusa"
import { Router } from "express"
import authenticate from "@medusajs/medusa/dist/api/middlewares/authenticate-customer"
import cors from "cors"
import { projectConfig } from "../../medusa-config"

export default () => {
  const router = Router()

  const corsOptions = {
    origin: projectConfig.store_cors.split(","),
    credentials: true,
  }

  router.options("/store/customers/is-b2b", cors(corsOptions))
  router.get("/store/customers/is-b2b", cors(corsOptions), authenticate(), async (req, res) => {
    if (!req.user) {
      return res.json({
        is_b2b: false
      })
    }

    const customerService: CustomerService = req.scope.resolve('customerService');

    const customer = await customerService
      .retrieve(req.user.customer_id, {
        relations: ['groups']
      })

    const is_b2b = customer.groups.some((group) => group.metadata.is_b2b === "true");

    return res.json({
      is_b2b
    })
  })

  return router;
}
```

In this file, you add a new storefront endpoint `/store/customers/is-b2b`. This endpoint checks if the currently logged-in customer belongs to a B2B customer group.

Looking back at when you created each customer group, you set in its `metadata` field `is_b2b` to `true`. In this endpoint, you retrieve the customer along with their customer groups, then check if any of its customer groups has the `is_b2b` field set to `true`.

### Test Endpoint

To test the endpoint, first, run the following command to transpile the TypeScript file into the `dist` directory:

```bash
npm run build
```

Then, start the Medusa server:

```bash
npm start
```

Using a tool like Postman, try to first login with a B2B customer then send a request to `localhost:9000/store/customers/is-b2b`. You should see that it returns `is_b2b` with the value `true`.

If you try sending that same request when the customer is logged-out, or when the customer does not belong to a B2B customer group, `is_b2b` in the response should be set to `false`.

## Next Steps

This concludes preparing your Medusa backend to be used for a B2B shopping experience. In the next part of the tutorial, you’ll learn how you can customize the Next.js storefront to implement the entire B2B flow for the customers.

You can also check out the following resources in our documentation to learn more about the different components used in this tutorial:

*   [Sales Channels](https://docs.medusajs.com/advanced/backend/sales-channels/) and [how to use them](https://docs.medusajs.com/advanced/backend/sales-channels/manage-admin).

*   [Customer Groups](https://docs.medusajs.com/advanced/backend/customer-groups/) and [how to use them](https://docs.medusajs.com/advanced/admin/use-customergroups-api).

*   [Price Lists](https://docs.medusajs.com/advanced/backend/price-lists/) and [how to use them](https://docs.medusajs.com/advanced/backend/price-lists/use-api).


> Should you have any issues or questions related to Medusa, then feel free to reach out to the Medusa team via [Discord](https://discord.gg/F87eGuwkTp).