'Getting null when I use context.query with getServerSideProps() in Nextjs?

Intro

Basically, I am building an e-commerce website. But I got stuck in this problem while saving the order details after I click payment button (although I had not used any payment gateway).

Problem

After selecting the items in cart , I fill the details in Checkout page and go to pay option to confirm order. In this checkout page I call addorder api which first do some checks(like whether item got out of stock or not) on the product in cart and then create new order.

After creating a new order in order DB (MOngo DB I am using), I goto order page like this with my orderId

res.redirect('/order/' + orderToAdd.orderId, 200);

err After clicking the pay button shown in the image above, it redirects to http://localhost:3000/order/814407650978 giving 404 (Not Found).

Thus when I fetch the order details from the MyOrder.js using context.query.orderId, from getServerSideProps() , I am getting null.

export async function getServerSideProps(context) {
  if (!mongoose.connections[0].readyState) {
    await mongoose.connect(process.env.MONGO_URI);
  }

  let order = await Order.findById({orderId:context.query.orderId})
  console.log("got = ", order);
  return {
    props: { order: JSON.parse(JSON.stringify(order)) }, // will be passed to the page component as props
  };
}

You can see full code below

Code

This is a complete order DB entry, where I am only sending the products object to MyOrder.js

complete order =  {
  email: '[email protected]',
  orderId: '869972292107',
  paymentInfo: 'No payment info',
  products: {
    'wear-the-chess-king-M-Red': {
      qty: 2,
      price: 599,
      name: 'King Chess Stylish Men’s t-Shirt',
      size: 'M',
      variant: 'Red'
    }
  },
  address: 'A-22\nVinayak campus',
  subtotal: 1198,
  status: 'Paid',
  _id: new ObjectId("626bd1bd01597b226fc76531"),
  createdAt: 2022-04-29T11:53:33.046Z,
  updatedAt: 2022-04-29T11:53:33.046Z,
  __v: 0
}

Checkout.js

import React, { useState } from "react";
import { AiFillPlusCircle, AiFillMinusCircle } from "react-icons/ai";
import { MdOutlinePayment } from "react-icons/md";
import { useRouter } from "next/router";
import "react-toastify/dist/ReactToastify.css";
import { toast, ToastContainer } from "react-toastify";
// Added payment AiOutlineGateway, but due to no merchant key, it is creating invalid checksum => hence push to different branch in both local & remote

const Checkout = ({ cart, clearCart, subtotal, addToCart, removeFromCart }) => {
  const [name, setName] = useState("");
  const [email, setEmail] = useState("");
  const [phone, setPhone] = useState("");
  const [address, setAddress] = useState("");
  const [pincode, setPincode] = useState("");

  const [city, setCity] = useState("");
  const [statemap, setStatemap] = useState("");

  const [disable, setDisable] = useState(true);

  const router = useRouter();

  const handleChange = async (e) => {
    if (e.target.name === "name") {
      setName(e.target.value);
    } else if (e.target.name === "email") {
      setEmail(e.target.value);
    } else if (e.target.name === "phone") {
      setPhone(e.target.value);
    } else if (e.target.name === "address") {
      setAddress(e.target.value);
    } else if (e.target.name === "pincode") {
      setPincode(e.target.value);
      if (e.target.value.length == 6) {
        let pins = await fetch(`${process.env.NEXT_PUBLIC_HOST}/api/pincode`);
        let pinjson = await pins.json();
        //console.log(pinjson)
        if (Object.keys(pinjson).includes(e.target.value)) {
          setCity(pinjson[e.target.value][0]);
          setStatemap(pinjson[e.target.value][1]);
        } else {
          setCity("");
          setStatemap("");
        }
      } else {
        setCity("");
        setStatemap("");
      }
    }

    if (name && email && phone && address && pincode) setDisable(false);
    else setDisable(true);

  };

  const initiateOrder = async () => {
    let oid = Math.floor(Math.random() * Date.now());
    const data = {
      cart: cart,
      subtotal: subtotal,
      oid: oid,
      email: email,
      name: name,
      address: address,
      pincode: pincode,
      phone: phone,
    };
    //console.log(JSON.stringify(data));
    let res = await fetch(`${process.env.NEXT_PUBLIC_HOST}/api/addorder`, {
      method: "POST", // or 'PUT'
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify(data),
    });
    let response = await res.json();
    //console.log("response from order- ", response);
    if (response.success === true) {
      localStorage.setItem("token", response.authToken);
      toast.success("Order Added Successfully!", {
        position: "bottom-center",
        autoClose: 2000,
        hideProgressBar: false,
        closeOnClick: true,
        pauseOnHover: true,
        draggable: true,
        progress: undefined,
      });
      setTimeout(() => {
        router.push("/order");
      }, 2500);
    } else {
      
      
      if(response.error === "err1"){  
        toast.error("Total price of your cart have changed accidently", {
          position: "bottom-center",
          autoClose: 2000,
          hideProgressBar: false,
          closeOnClick: true,
          pauseOnHover: true,
          draggable: true,
          progress: undefined,
        });
      }
      else if(response.error === "err2"){
        toast.error("Some items in your cart went out of stock !", {
          position: "bottom-center",
          autoClose: 2000,
          hideProgressBar: false,
          closeOnClick: true,
          pauseOnHover: true,
          draggable: true,
          progress: undefined,
        });
      }
      else if(response.error === "err5"){
        toast.error("Prices of some of the items in your cart have changed !", {
          position: "bottom-center",
          autoClose: 2000,
          hideProgressBar: false,
          closeOnClick: true,
          pauseOnHover: true,
          draggable: true,
          progress: undefined,
        });
      }
      else {
        toast.error("Error in Adding Order !", {
          position: "bottom-center",
          autoClose: 2000,
          hideProgressBar: false,
          closeOnClick: true,
          pauseOnHover: true,
          draggable: true,
          progress: undefined,
        });
      }
    }
  };

  return (
    <>
      <div className="container px-2 sm:m-auto">
        <ToastContainer
          position="bottom-center"
          autoClose={2000}
          hideProgressBar={false}
          newestOnTop={false}
          closeOnClick
          rtl={false}
          pauseOnFocusLoss
          draggable
          pauseOnHover
        />
        <h1 className="text-xl md:text-3xl text-center my-8 font-semibold">
          Checkout
        </h1>

        {/* part 1 */}
        <h2 className="text-xl my-4 font-semibold">1. Delivery Details</h2>

        <div className="mx-auto flex">
          <div className="px-2 w-1/2">
            <div className="mb-4">
              <label htmlFor="name" className="leading-7 text-sm text-gray-600">
                Name
              </label>
              <input
                type="name"
                id="name"
                name="name"
                value={name}
                onChange={handleChange}
                className="w-full bg-white rounded border border-gray-300 focus:border-blue-500 focus:ring-2 focus:ring-blue-200 text-base outline-none text-gray-700 py-1 px-3 leading-8 transition-colors duration-200 ease-in-out"
              />
            </div>
          </div>
          <div className="px-2 w-1/2">
            <div className=" mb-4">
              <label
                htmlFor="email"
                className="leading-7 text-sm text-gray-600"
              >
                Email
              </label>
              <input
                type="email"
                id="email"
                name="email"
                value={email}
                onChange={handleChange}
                className="w-full bg-white rounded border border-gray-300 focus:border-blue-500 focus:ring-2 focus:ring-blue-200 text-base outline-none text-gray-700 py-1 px-3 leading-8 transition-colors duration-200 ease-in-out"
              />
            </div>
          </div>
        </div>

        <div className="px-2 w-full">
          <div className=" mb-4">
            <label
              htmlFor="address"
              className="leading-7 text-sm text-gray-600"
            >
              Address
            </label>

            <textarea
              id="address"
              name="address"
              value={address}
              onChange={handleChange}
              className="w-full bg-white rounded border border-gray-300 focus:border-blue-500 focus:ring-2 focus:ring-blue-200 text-base outline-none text-gray-700 py-1 px-3 leading-8 transition-colors duration-200 ease-in-out"
            />
          </div>
        </div>

        <div className="mx-auto flex">
          <div className="px-2 w-1/2">
            <div className="mb-4">
              <label
                htmlFor="phone"
                className="leading-7 text-sm text-gray-600"
              >
                Phone
              </label>
              <input
                type="text"
                id="phone"
                name="phone"
                value={phone}
                onChange={handleChange}
                className="w-full bg-white rounded border border-gray-300 focus:border-blue-500 focus:ring-2 focus:ring-blue-200 text-base outline-none text-gray-700 py-1 px-3 leading-8 transition-colors duration-200 ease-in-out"
              />
            </div>
          </div>
          {/* city */}
          <div className="px-2 w-1/2">
            <div className=" mb-4">
              <label
                htmlFor="pincode"
                className="leading-7 text-sm text-gray-600"
              >
                Pincode
              </label>
              <input
                type="text"
                id="pincode"
                name="pincode"
                value={pincode}
                onChange={handleChange}
                className="w-full bg-white rounded border border-gray-300 focus:border-blue-500 focus:ring-2 focus:ring-blue-200 text-base outline-none text-gray-700 py-1 px-3 leading-8 transition-colors duration-200 ease-in-out"
              />
            </div>
          </div>
        </div>

        <div className="mx-auto flex">
          <div className="px-2 w-1/2">
            <div className="mb-4">
              <label
                htmlFor="state"
                className="leading-7 text-sm text-gray-600"
              >
                State
              </label>
              <input
                type="text"
                id="state"
                name="state"
                value={statemap}
                className="w-full bg-white rounded border border-gray-300 focus:border-blue-500 focus:ring-2 focus:ring-blue-200 text-base outline-none text-gray-700 py-1 px-3 leading-8 transition-colors duration-200 ease-in-out"
                onChange={handleChange}
              />
            </div>
          </div>
          <div className="px-2 w-1/2">
            <div className=" mb-4">
              <label htmlFor="city" className="leading-7 text-sm text-gray-600">
                City
              </label>
              <input
                type="text"
                id="city"
                name="city"
                value={city}
                className="w-full bg-white rounded border border-gray-300 focus:border-blue-500 focus:ring-2 focus:ring-blue-200 text-base outline-none text-gray-700 py-1 px-3 leading-8 transition-colors duration-200 ease-in-out"
                onChange={handleChange}
              />
            </div>
          </div>
        </div>

        {/* part 2 */}
        <h2 className="text-xl my-4 font-semibold">2. Review Cart Items</h2>

        <div className="z-10 sideCart  p-6 mx-2 my-4 bg-blue-100 border-[1px] border-blue-800 rounded">
          <ol className="list-decimal font-semibold">
            {Object.keys(cart).length === 0 && (
              <div className="mt-5 text-center text-xl font-extralight">
                Your Cart is Empty :(
              </div>
            )}
            {
              //k is cart {k: {name, price,qty,size,variant} }
              Object.keys(cart).map((k) => {
                return (
                  <li key={k}>
                    <div className="flex item my-5 items-center justify-between">
                      <div className=" font-semibold">
                        {cart[k].name} [{cart[k].variant} - {cart[k].size}]
                      </div>
                      <div className="w-1/3 font-semibold flex items-center justify-center">
                        <AiFillPlusCircle
                          onClick={() =>
                            addToCart(
                              k,
                              1,
                              cart[k].price,
                              cart[k].name,
                              cart[k].size,
                              cart[k].variant
                            )
                          }
                          className="text-blue-700 text-xl cursor-pointer"
                        />
                        <span className="mx-2 text-sm">{cart[k].qty}</span>
                        <AiFillMinusCircle
                          onClick={() =>
                            removeFromCart(
                              k,
                              1,
                              cart[k].price,
                              cart[k].name,
                              cart[k].size,
                              cart[k].variant
                            )
                          }
                          className="text-blue-700 text-xl cursor-pointer"
                        />
                      </div>
                    </div>
                  </li>
                );
              })
            }
          </ol>

          <span className="subtotal text-xl font-extrabold">
            Subtotal : ₹ {subtotal} /-
          </span>
        </div>

        <button
          disabled={disable}
          onClick={initiateOrder}
          className="disabled:bg-blue-300 flex text-white bg-blue-500 border-0 py-2 px-3 focus:outline-none hover:bg-blue-600 rounded text-base mx-2  my-4"
        >
          <MdOutlinePayment className="m-1" />
          Pay ₹ {subtotal}
        </button>
      </div>
    </>
  );
};

export default Checkout;

/api/addorder.js

import connectDB from "../../middleware/mongoose";
import Order from "../../models/Order";
import Product from "../../models/Product";

const handler = async (req, res) => {
  if (req.method === "POST") {
    let product,
      sumtotal = 0;
    for (let item in req.body.cart) {
      //got key(slug) as item
      console.log(item);
      sumtotal += req.body.cart[item].qty * req.body.cart[item].price;
      product = await Product.findOne({ slug: item });

      //TODO1: check if cart is tampered or not
      if (product.price !== req.body.cart[item].price) {
        res.status(500).json({ success: false, error: "err1" });
        //console.log("ResT - ",res.json)
        return;
      }

      //TODO2: check if cart items are out of stock
      if (product.availableQty < req.body.cart[item].qty) {
        res.status(500).json({ success: false, error: "err2" });
        return;
      }
    }
    //console.log("sumtotal = ",sumtotal," and subtotal =  ",req.body.subtotal)
    if (sumtotal != req.body.subtotal) {
      res.status(500).json({ success: false, error: "err5" });
      //console.log("ResP - ",res)
      return;
    }

    //TODO3: check if details are valid or not
    let orderToAdd = new Order({
      email: req.body.email,
      orderId: req.body.oid,
      address: req.body.address,
      subtotal: req.body.subtotal,
      products: req.body.cart,
      status: "Paid",
    });
    await orderToAdd.save();

    let ordered_products = orderToAdd.products;
    console.log("Orederd pro = ", ordered_products);
    console.log("complete order = ", orderToAdd)
    for (let slug in ordered_products) {
      await Product.findOneAndUpdate(
        { slug: slug },
        { $inc: { availableQty: -ordered_products[slug].qty } }
      );
    }


    res.redirect('/order/' + orderToAdd.orderId, 200)
  } else {
    // Handle any other HTTP method
    res.status(400).json({ success: false });
  }
};

export default connectDB(handler);

MyOrder.js

import React from "react";
import mongoose from "mongoose";
import Order from "../models/Order";

//TODO: To change the orderdetail fecthing resource from cart,subtotal to orders DB (user specific orders)

const MyOrder = ({ order }) => {
  console.log("order = ",order)
  const products = order.products;
  console.log(order,products);
  return (
    <div>
      <section className="text-gray-600 body-font overflow-hidden">
        <div className="container px-5 py-24 mx-auto">
          <div className="lg:w-4/5 mx-auto flex flex-wrap">
            <div className="lg:w-1/2 w-full lg:pr-10 lg:py-6 mb-6 lg:mb-0">
              <h2 className="text-sm title-font text-gray-500 tracking-widest">
                CHESSWEAR.COM
              </h2>
              <h1 className="text-gray-900 text-3xl title-font font-medium mb-4">
                Order Id: 2242434535
              </h1>

              <p className="leading-relaxed mb-4 text-green-700 rounded-3xl bg-green-100 hover:bg-green-100/70 p-3 border-[1px] border-green-700 inline-flex items-center justify-center ">
                Your Order has been successfully placed !
              </p>
              <div className="flex mb-4 justify-evenly">
                <a className="flex-grow w-12 py-2 text-lg px-1">Description</a>
                <a className="flex-grow  py-2 text-lg px-1">Quantity</a>
                <a className="flex-grow  py-2 text-lg px-1">Price</a>
              </div>
              {Object.keys(cart).map((key) => {
                return (
                  <div key={key} className="flex border-t border-gray-200 py-2">
                    <span className="text-gray-500 w-28 ">
                      {cart[key].name} ({cart[key].size} - {cart[key].variant})
                    </span>
                    <span className="m-auto text-gray-900">
                      {cart[key].qty}
                    </span>
                    <span className="mr-auto mt-auto mb-auto text-gray-900">
                     ₹ {cart[key].price}
                    </span>
                  </div>
                );
              })}
              <div className="flex py-2 md:py-4">
                <span className="title-font font-medium text-2xl text-gray-900">
                  Subtotal :  <span className="ml-4 text-red-900 font-bold text-3xl leading-tight">₹ {subtotal} /-</span>
                </span>
                <button className="flex ml-auto text-white bg-blue-500 border-0 py-2 px-6 focus:outline-none hover:bg-blue-600 rounded">
                  Track Order
                </button>
              </div>
            </div>
            <img
              alt="ecommerce"
              className="lg:w-1/2 w-full lg:h-auto h-64 object-cover object-center rounded"
              src="https://dummyimage.com/400x400"
            />
          </div>
        </div>
      </section>
    </div>
  );
};

export async function getServerSideProps(context) {
  if (!mongoose.connections[0].readyState) {
    await mongoose.connect(process.env.MONGO_URI);
  }

  let order = await Order.findById({orderId:context.query.orderId})
  console.log("got = ", order);
  return {
    props: { order: JSON.parse(JSON.stringify(order)) }, // will be passed to the page component as props
  };
}

export default MyOrder;

File Structure

enter image description here Please help me???



Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source