Back to Top

How To Create Product Page Using Odoo API In NextJs.

Updated 27 September 2023

How to use Next.JS with Odoo – I’m happy to help you with writing a blog post about creating a product page using the Odoo API In NextJS.

First, we’ll have some discussion about NextJS and begin by introducing the concept of Odoo and its API.

What is NextJS?

Next Js is a full-stack web development react framework. Which is a free and open-source front-end react library framework for building user interfaces based on UI components.

NextJs Provide all the feature to develop Several types of websites and applications that can be built  Static and Server Rendering, Static-side Regeneration, Typescript support, and Routing.

We need to Set up NextJs Project to start Also we are using Tailwind CSS within the Project.

Searching for an experienced
Odoo Company ?
Find out More

Your project directory structure must be like this.

.
├── pages/
│   ├── api
│         ├── hello.js
│   ├── app.js
│   ├── _document.js
│   └── index.js
├── public/
│   ├── favicon.ico
│   ├── next.svg
│   └── vercel.svg
├── styles/
│   └── global.css
├── next.config.js
├── package-lock.json
├── package.json
├── postcss.config.js
├── README.md
├── tailwind.config.js
└── tsconfig.js

Understanding the Odoo API.

Basically, the Odoo API is normally the Rest API which provides the e-commerce product services and serves as the bridge between your application and the Odoo Platform.

Real-World Use Cases.

  • E-commerce Integration.
  • Inventory Management.

Understanding the Odoo API endPoints.

Odoo offers a range of API endpoints that allow you to perform different actions like ‘products’, ‘category-list’, and ‘customer’ but we’ll work with the ‘product-list’ endpoint.

To Retrieve product data, you’ll make a ‘GET’ or ‘POST’ request from appropriate API endpoints to the appropriate API endpoints. But you would make a request to the ‘product-list’. You can also filter and sort the data based on your requirements.

Getting Started:-

We’re going to create a file named .env in the root directory and add the following code.

//------------.env--------------//

MODE=PRODUCTION
CLIENT_TOKEN=<Authenticate_Token>
API_URL=https://Myodoo.com/
API_VERSION=<Version_Name>
  • We also need to define the environment variables in our next.config.js file otherwise we will not be able to use these variables at the front end.
//------------next.config.js-----------//

/** @type {import('next').NextConfig} */
const path = require('path');
const withBundleAnalyzer = require('@next/bundle-analyzer')({
  enabled: process.env.ANALYZE === 'true',
});

const nextConfig = {
  env: {
    CLIENT_TOKEN: process.env.CLIENT_TOKEN,
    API_URL: process.env.API_URL,
    API_VERSION: process.env.API_VERSION,
  },
  reactStrictMode: true,
};

module.exports = withBundleAnalyzer(nextConfig);

Make sure to have the necessary API endpoints URL of ‘product-list’, authenticate, and other parameters. we’ll need to use the fetch function or a library like Axios to retrieve data from the Odoo API.

API Response:

Odoo-Products-API-Response

Create a Route file to Display the Product:

We’re going to create a file named [urlKey].js in pages/product/[urlKey].js and add the following code.

//---------------pages/product/[urlkey].js-----------//

import Image from 'next/image';
import { useState } from 'react';

const Product = ({ product }) => {
  const { thumbnail, price_range, sku } = product;
  const [addtocart, setAddtocart] = useState(1);
  const add = () => {
    setAddtocart(addtocart + 1);
  };
  const sub = () => {
    addtocart > 1 && setAddtocart(addtocart - 1);
  };

  return (
    <div class="grid grid-cols-5 gap-4 w-[85%] mx-auto my-5">
      <div className="col-span-2 border border-1 border-solid border-slate-400 rounded">
        <Image src={thumbnail?.id} width={500} height={500} />
      </div>
      <div className="col-span-3 mx-10">
        <div className="">
          <div display="grid">
            <p className="font-[500] text-[2.5rem]">{product.name || ''}</p>
            <div className="flex justify-between ">
              <p className="text-price" sx={{ paddingTop: 1.5 }}>
                <span className="font-semibold">
                  $ {price_range?.minimum_price?.regular_price?.value}
                </span>

                <s className="pl-4 italic font-light text-fadedText">
                  {price_range?.discount?.amount_off}
                </s>
              </p>
              <p variant="body1" className="mt-7">
                Sku : {sku}
              </p>
            </div>
            <div className="flex">
              <button
                onClick={sub}
                aria-label="increment"
                className="text-white w-10 rounded-l h-8 border-0 cursor-pointer bg-secondary hover:bg-brand hover:contrast-75"
              >
                -
              </button>
              <input
                max={6}
                type="text"
                className="relative w-14 border-[1px] border-gray flex items-center px-3 font-semibold text-center text-gray-700   outline-none cursor-default -z-10 readonly focus:outline-none text-md hover:text-black focus:text-black md:text-base"
                min={1}
                value={addtocart}
                id="quantity"
                placeholder="0"
              />

              <button
                aria-label="increment"
                className="text-white w-10 h-8 rounded-r border-0 cursor-pointer bg-secondary hover:bg-brand hover:contrast-75"
                onClick={add}
              >
                +
              </button>
            </div>

            <p className="pt-3 text-hoverEffect text-[16px] ">
              {product.short_description?.html ||
                ''}
            </p>
          </div>

          <button
            color="secondary"
            variant="contained"
            className="w-full py-4 mx-auto"
            type="submit"
          >
            Add to cart
          </button>
        </div>
      </div>
    </div>
  );
};

export default Product;

export async function getStaticPaths() {
  let paths = [];
  try {
    const data = await fetchProduct();
    const products = data?.products?.items || [];
    paths = products.map((product) => ({
      params: { urlKey: product?.url_key || '404' },
    }));
  } catch (error) {
    console.error('Error fetching data from API:', error);
  }

  return { paths, fallback: 'blocking' };
}

export async function getStaticProps({ params }) {
  const productInfo = await fetchProduct(params?.urlKey);

  const product = productInfo?.products?.items?.[0] || null;

  if (product.length > 0) {
    return {
      notFound: true,
    };
  }
  return {
    props: {
      product: product,
    },
    revalidate: 100,
  };
}

const fetchProduct = async (urlKey = null) => {
const URL = `${process.env.API_URL}api/${process.env.API_VERSION}/product-list`;
  const filter = {};
  if (urlKey) {
    filter.url_key = { eq: urlKey };
  }
  const response = await fetch(URL, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      Authenticate: `${process.env.CLIENT_TOKEN}`, // Here, We need to pass a token in Header to authenticate and use Oddo API.
    },
    body: JSON.stringify({
      filter: filter,
    }),
  });
  if (response.status !== 200) {
    throw new Error(`API request failed with status ${response.status}`);
  }
  return response.json();
};

You can see the result on http://localhost:3000

Start your Odoo Headless Development with Webkul.

Happy Coding!!

. . .

Leave a Comment

Your email address will not be published. Required fields are marked*


Be the first to comment.

Back to Top

Message Sent!

If you have more details or questions, you can reply to the received confirmation email.

Back to Home