To have a successful e-commerce strategy, you need to publish your products on different platforms like Amazon, eBay, and Shopify to make your products available in a wider market. One of the biggest challenges of this strategy is you don't have a single source of truth for your products, and when you have a high number of products, it becomes really hard to manage one product and its attributes on multiple platforms.
In this tutorial, we will see how we can use Strapi to solve this problem.
In this tutorial, we'll learn how having a single source of truth can help with product management. We start by creating a content type for products. Then, we'll import product details from two different sources and save the theme in Strapi. Then, we'll enrich the products in Strapi. We will also build a storefront for our products using Strapi as Product Information Manager.
A Product Information Management (PIM) solution is a business application that provides a single place to collect, manage, and enrich your product information, create a product catalog, and distribute it to your sales and eCommerce channels. A PIM solution makes it faster and easier to create and deliver compelling product experiences.
In this tutorial, we'll build a Product Information Manager (PIM) with Strapi, importing products details from 2 data sources:
You can find a live demo of the application here.
The first thing in this stage is the installation and setup of Strapi. To do this, follow the instructions below:
git clone https://github.com/amirtds/strapi-pmi
strapi-pmi
directory in your favorite editor and rename the .env.example
file to .env
. Then, open the .env
file and add the following line:1 API_TOKEN_SALT=tempkey
Note: For this tutorial, we are using a default value for environment variables; for production, you should change them.
yarn install && yarn develop
After the command is executed, visit http://localhost:1337/ and create an admin user.
The next step is to employ the content-type builder. Follow the instructions below:
1. After successfully logging in to Strapi, on the left sidebar, under the PLUGINS section, click on the Content-Type Builder button.
2. Under the COLLECTION TYPES click on the Create new collection type link and for the Display name, enter product
.
3. Let's create fields for our new content type. The product content type will have the following fields:
For more information about the content-type builder please visit Strapi Doc for Introduction to the Content-Type Builder.
Next, let's enable the find
and findOne
queries for products via Strapi GraphQL API.
find
and findOne
. As we mentioned earlier, we are going to fetch products data from two sources, Dummyjson API and Fakestoreapi API.
Let's create a cron job to fetch these data every day the product doesn't exist in Strapi, we use product
api service to create a new product.
strapi-pmi
in your editor, in the config directory, create a new file called cront-tasks.js
, and add the following code to it:1 const fetch = require('node-fetch');
2
3 module.exports = {
4 '0 0 0 * * *': async ({ strapi }) => {
5 let products = []
6 // Get all products from dummyjson API
7 const dummyjsonResponse = await fetch('https://dummyjson.com/products');
8 const dummyProducts = await dummyjsonResponse.json();
9 // Append each product to the products array
10 products.push(...dummyProducts.products);
11 // Get all products from fakestoreapi API
12 fakeStoreResponse = await fetch('https://fakestoreapi.com/products');
13 fakeStoreProducts = await fakeStoreResponse.json();
14 // Append each product to the products array
15 products.push(...fakeStoreProducts);
16 // Loop through each product
17 products.forEach(async (product) => {
18 // Check if the product already exists
19 const existingProduct = await strapi.service("api::product.product").find({
20 filters: {
21 name: product.title
22 }
23 });
24 // If the product exists, log it
25 if (existingProduct.results.length > 0){
26 console.log(`Product ${product.title} already exists`)
27 }
28 else {
29 // If the product doesn't exist, create it
30 // create the image
31 let image = ""
32 product.image !== undefined ? image = product.image : product.images !== undefined ? image = product.images[0] : image = ""
33 const createdProduct = strapi.service("api::product.product").create({
34 data : {
35 name: product.title,
36 description: product.description,
37 imageUrl: image,
38 price: product.price,
39 brand: product.brand || '',
40 color: product.color || '',
41 size: product.size || '',
42 quantity: product.quantity || 0,
43 category: product.category || '',
44 }
45 });
46 }
47 });
48 }
49 }
Take note of the code comments.
server.js
and replace its content with the following:1 const cronTasks = require("./cron-tasks");
2
3 module.exports = ({ env }) => ({
4 host: env('HOST', '0.0.0.0'),
5 port: env.int('PORT', 1337),
6 app: {
7 keys: env.array('APP_KEYS')
8 },
9 cron: {
10 enabled: true,
11 tasks: cronTasks
12 },
13 });
To learn more about Cron Jobs in Strapi, please visit Strapi Doc.
To initialize our server with product data we can run similar code in bootstrap function to create products.
index.js
file. replace bootstrap(/*{ strapi }*/) {},
with the following:1 bootstrap({ strapi }) {
2 const fetch = require('node-fetch');
3 let products = []
4 // Get all products from dummyjson API and append them to the products array
5 fetch('https://dummyjson.com/products').then(response => response.json()).then(dummyProducts => {
6 products.push(...dummyProducts.products);
7 // Get all products from fakestoreapi API and append them to the products array
8 fetch('https://fakestoreapi.com/products').then(response => response.json()).then(fakeStoreProducts => {
9 products.push(...fakeStoreProducts);
10 // Loop through each product
11 products.forEach(async (product) => {
12 // Check if the product already exists
13 const existingProduct = await strapi.service("api::product.product").find({
14 filters: {
15 name: product.title
16 }
17 });
18 // If the product exists, log it
19 if (existingProduct.results.length > 0){
20 console.log(`Product ${product.title} already exists`)
21 }
22 else {
23 // If the product doesn't exist, create it
24 // create the image
25 let image = ""
26 product.image !== undefined ? image = product.image : product.images !== undefined ? image = product.images[0] : image = ""
27 const createdProduct = strapi.service("api::product.product").create({
28 data : {
29 name: product.title,
30 description: product.description,
31 imageUrl: image,
32 price: product.price,
33 brand: product.brand || '',
34 color: product.color || '',
35 size: product.size || '',
36 quantity: product.quantity || 0,
37 category: product.category || '',
38 }
39 });
40 }
41 });
42 });
43 });
44 }
Take note of the code comments.
Let's enrich one of our products with more data.
1. Go to the Product page in the Content Manager and search for Samsung 49-Inch
product.
2. Open the product page and add the following data:
brand
: Samsung
color
: Black
size
: 49 Inch
quantity
: 10
discount_price
: $799.99
discount_start_date
: 2023-01-01
discount_end_date
: 2023-01-05
published
: true
currency
: USD
bestseller
: true
featured
: true
As you can see, all the fields we filled were empty because we didn't have any data for them in our data sources.
In this step, we will publish our products into a custom storefront powered by Nextjs. Before continuing, please make sure your Strapi server is running.
Clone the storefront project from GitHub.
git clone -b feat/frontend-without-strapi https://github.com/amirtds/my-store-frontend.git
The UI of the storefront is already built. In this step, we are going to integrate our PMI into it to make it work.
.env
at the root of the project with the following content:1 STRAPI_GRAPHQL_ENDPOINT = http://localhost:1337/graphql
In the project, we are going to query Strapi Graphql API for products and we are also going to filter products to find featured products to show in the Hero section of the storefront. Now open http://localhost:1337/graphql in your browser (this is Strapi Graphql API and it opens a playground for you to test queries). In the left section paste the following query:
1 query {
2 products(pagination: {pageSize: 50}) {
3 data {
4 id
5 attributes {
6 name
7 description
8 price
9 published
10 brand
11 color
12 size
13 quantity
14 discount_price
15 discount_start_date
16 discount_end_date
17 category
18 imageUrl
19 currency
20 bestseller
21 featured
22 }
23 }
24 }
25 }
Now let's test another query we are going to use in our storefront.
1 query {
2 products(filters: {featured: {eq: true}}) {
3 data {
4 id
5 attributes {
6 name
7 description
8 price
9 published
10 brand
11 color
12 size
13 quantity
14 discount_price
15 discount_start_date
16 discount_end_date
17 category
18 imageUrl
19 currency
20 bestseller
21 featured
22 }
23 }
24 }
25 }
Let's go back to our editor and open the storefront project.
src/pages/index.js
file, look for const FEATURED_PRODUCT_QUERY =
and replace the "REPLACE_WITH_QUERY"
with the following:1 gql`
2 query {
3 products(filters: {featured: {eq: true}}) {
4 data {
5 id
6 attributes {
7 name
8 description
9 price
10 published
11 brand
12 color
13 size
14 quantity
15 discount_price
16 discount_start_date
17 discount_end_date
18 category
19 imageUrl
20 currency
21 bestseller
22 featured
23 }
24 }
25 }
26 }
27 `;
const ALL_PRODUCTS_QUERY =
and replace the "REPLACE_WITH_QUERY"
with the following:1 gql`
2 query {
3 products(pagination: {pageSize: 50}) {
4 data {
5 id
6 attributes {
7 name
8 description
9 price
10 published
11 brand
12 color
13 size
14 quantity
15 discount_price
16 discount_start_date
17 discount_end_date
18 category
19 imageUrl
20 currency
21 bestseller
22 featured
23 }
24 }
25 }
26 }
27 `;
src/pages/api/products.js
file and replace the "REPLACE_WITH_QUERY"
with the following:1 gql`
2 query {
3 products(pagination: {pageSize: 50}) {
4 id
5 attributes {
6 name
7 description
8 price
9 published
10 brand
11 color
12 size
13 quantity
14 discount_price
15 discount_start_date
16 discount_end_date
17 category
18 imageUrl
19 currency
20 bestseller
21 featured
22 }
23 }
24 }
25 `;
my-store-frontend
folder and run the following command to install all the dependencies and run storefront: npm install && npm run dev
In this tutorial, we have learned how we can use Strapi to build a Product Information Manager to have a single source of truth for our products. We used the bootstrap function and cron job to populate product data from different data sources, we could enrich our products with more data that doesn't exist in our data sources. Finally, we enabled Graphql for our products and streamed them into our Nextjs storefront.
We only streamed the products to one sales channel, but we can duplicate the process in Step 5 to stream products to multiple sales channels like Shopify, Magento, etc.
As a practice, you can try creating a Shopify store and stream products to your Shopify store.
I'm a full stack engineer who loves the challenges of working with cutting-edge technologies like React, Nextjs, Strapi.