Search code examples
node.jsshippo

Cannot read property 'create' of undefined error when integrating Shippo


I am working on a product using node.js in my backend and react.js in my frontend. In this project, I am trying to integrate Shippo. They provide api information for the backend, which I have tried to use/follow. I am currently getting a cannot read property 'create' of undefined error, which is a POST 500 Internal Server Error from the backend, but I an unsure why I am getting this error and if I am integrating this system correctly. I would really appreciate any help or advice on what I could be doing wrong. Thank you!

Backend:

Shippo.js

'use strict';

import express from 'express';
import shippo from 'shippo';
const shippoToken = process.env.SHIPPO_TOKEN || ''

const shippoRouter = express.Router();



shippoRouter.post(
    '/shippinginfo', 
function (req, res) {

var addressFrom  = {
    "name": "Jane Smith",
    "street1": "500 Riveride Dr",
    "city": "New York",
    "state": "NY",
    "zip": "10002",
    "country": "US"
};

const to = req.body
var addressTo = shippo.address.create({
    "name": to.fullName,
    "street1": to.address1,
    "street2": to.address2,
    "city": to.city,
    "state": to.state,
    "zip": to.postalCode,
    "country": to.country,
    "phone": to.phoneNumber,
    "email":to.email,
    "validate": true,
}, function(err, address) {
    // asynchronously called
});
    // asynchronously called

console.log(addressFrom)
console.log(addressTo)

var parcel = {
    "length": "5",
    "width": "5",
    "height": "5",
    "distance_unit": "in",
    "weight": "2",
    "mass_unit": "lb"
};

/*
shippo.shipment.create({
    "address_from": addressFrom,
    "address_to": addressTo,
    "parcels": [parcel],
    "async": false
}, function(err, shipment){
    // asynchronously called
});

shippo.shipment.rates('5e40ead7cffe4cc1ad45108696162e42');
//var rate = shipment.rates[0];

// Purchase the desired rate.
shippo.transaction.create({
    "rate": rate.object_id,
    "label_file_type": "PDF_4x6",
    "async": false
}, function(err, transaction) {
   // asynchronous callback
});*/

});

export default shippoRouter;

server.js

import express from 'express';
import cors from 'cors';
import mongoose from 'mongoose';
import dotenv from 'dotenv';
import path from 'path';
import shippoRouter from './shippo.js';

dotenv.config();

const app = express();
app.use(cors()); //and this
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

mongoose.connect(process.env.MONGODB_URL || 'mongodb://localhost/AM', {
    useNewUrlParser: true,
    useUnifiedTopology: true,
    useCreateIndex: true,
});

app.use('/api/shippo', shippoRouter);
app.get('/', (req, res) => {
    res.send('Server is ready');
});

app.use((err, req, res, next) => {
    res.status(500).send({ message: err.message });
});

const port = process.env.PORT || 5000;
app.listen(port, () => {
    console.log(`Serve at http://localhost:${port}`);
});

How I tried to get the shipping information from my frontend using axios Frontend:

ShippingAddressScreen.js

import React, { useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { saveShippingAddress } from '../actions/cartActions';
import CheckoutSteps from '../components/CheckoutSteps';
import { shippingInfo } from '../actions/shippingActions';

export default function ShippingAddressScreen(props) {
  const userSignin = useSelector((state) => state.userSignin);
  const { userInfo } = userSignin;
  const cart = useSelector((state) => state.cart);
  const { shippingAddress } = cart;
  if (!userInfo) {
    props.history.push('/signin');
  }
  const [fullName, setFullName] = useState(shippingAddress.fullName);
  const [address1, setAddress1] = useState(shippingAddress.address1);
  const [address2, setAddress2] = useState(shippingAddress.address2);
  const [city, setCity] = useState(shippingAddress.city);
  const [state, setState] = useState(shippingAddress.state);
  const [postalCode, setPostalCode] = useState(shippingAddress.postalCode);
  const [country, setCountry] = useState(shippingAddress.country);
  const [phoneNumber, setPhoneNumber] = useState('');
  const [email, setEmail] = useState('');
  const name = fullName;
  const street1 = address1;
  const street2 = address2;
  const zip = postalCode;
  const phone = phoneNumber;
  const dispatch = useDispatch();
  const submitHandler = (e) => {
    e.preventDefault();
    dispatch(
      saveShippingAddress({ fullName, address1, address2, city, state, postalCode, country })
    );
    props.history.push('/payment');
    dispatch(
      shippingInfo({ name, street1, street2, city, state, zip, country, phone, email })
    );
  };
  

 /* const shippingInfo = (e) => {
    e.preventDefault();
    
  };*/
  return (
    <div>
      <CheckoutSteps step1 step2></CheckoutSteps>
      <form className="form" onSubmit={submitHandler}>
        <div>
          <h1>Shipping Address</h1>
        </div>
        <div>
          <label htmlFor="fullName">Full Name</label>
          <input
            type="text"
            id="fullName"
            placeholder="Enter full name"
            value={fullName}
            onChange={(e) => setFullName(e.target.value)}
            required
          ></input>
        </div>
        <div>
          <label htmlFor="address1">Address</label>
          <input
            type="text"
            id="address1"
            placeholder="Enter address"
            value={address1}
            onChange={(e) => setAddress1(e.target.value)}
            required
          ></input>
        </div>
        <div>
          <label htmlFor="address2">Address</label>
          <input
            type="text"
            id="address2"
            placeholder="Enter address"
            value={address2}
            onChange={(e) => setAddress2(e.target.value)}
          ></input>
        </div>
        <div>
          <label htmlFor="city">City</label>
          <input
            type="text"
            id="city"
            name="city"
            placeholder="Enter city"
            value={city}
            onChange={(e) => setCity(e.target.value)}
            required
          ></input>
        </div>
        <div>
          <label htmlFor="state">State</label>
          <input
            type="text"
            id="state"
            placeholder="Enter state"
            value={state}
            onChange={(e) => setState(e.target.value)}
            required
          ></input>
        </div>
        <div>
          <label htmlFor="postalCode">Postal Code</label>
          <input
            type="text"
            id="postalCode"
            placeholder="Enter postal code"
            value={postalCode}
            onChange={(e) => setPostalCode(e.target.value)}
            required
          ></input>
        </div>
        <div>
          <label htmlFor="country">Country</label>
          <input
            type="text"
            id="country"
            placeholder="Enter country"
            value={country}
            onChange={(e) => setCountry(e.target.value)}
            required
          ></input>
        </div>
        <div>
          <label htmlFor="phoneNumber">Phone Number</label>
          <input
            type="text"
            id="phoneNumber"
            placeholder="Enter Phone Number"
            value={phoneNumber}
            onChange={(e) => setPhoneNumber(e.target.value)}
            required
          ></input>
        </div>
        <div>
          <label htmlFor="email">Email</label>
          <input
            type="text"
            id="email"
            placeholder="Enter Email"
            value={email}
            onChange={(e) => setEmail(e.target.value)}
            required
          ></input>
        </div>
         <div>
           <button className="primary" type="submit">
            Continue
          </button>
        </div>
      </form>
    </div>
  );
}

ShippingActions.js

import Axios from 'axios';
import {
  SHIPPING_ADDRESS_FAIL,
  SHIPPING_ADDRESS_REQUEST,
  SHIPPING_ADDRESS_SUCCESS,
} from '../constants/shippingConstants';

export const shippingInfo = (name, street1, street2, city, state, zip, country, phone, email) => async (dispatch) => {
    dispatch({ type: SHIPPING_ADDRESS_REQUEST, payload: { name, email} });
    try {
      const { data } = await Axios.post('/api/shippo/shippinginfo', {
        name,
        street1,
        street2,
        city,
        state,
        zip,
        country,
        phone,
        email,
      });
      dispatch({ type: SHIPPING_ADDRESS_SUCCESS, payload: data });
    } catch (error) {
      dispatch({
        type: SHIPPING_ADDRESS_FAIL,
        payload:
          error.response && error.response.data.message
            ? error.response.data.message
            : error.message,
      });
    }
  };  

I also have a shippingReducer, shippingConstants, and included the reducer in my store.js.

Also, if this is helpful https://goshippo.com/docs/reference/js#overview is Shippo's Api Information.


Solution

  • I am currently getting a cannot read property 'create' of undefined error

    From the code you shared, it looks like you are not initializing the shippo module correctly in Shippo.js, therefore shippo.create method does not exist.

    In your code, you are importing the shippo module, but not initializing it with the key:

    import shippo from 'shippo';
    const shippoToken = process.env.SHIPPO_TOKEN || ''
    
    // <omitted>
    
    var addressTo = shippo.address.create({
    // <omitted>
    

    Note that shippoToken remains unused in your file.

    In the Shippo library documentation, they have the following example:

    var shippo = require('shippo')('<YOUR_PRIVATE_KEY>');
    

    Since you are using ES modules, you can't use this one-liner, so try something like this instead:

    import shippo from 'shippo';
    const shippoToken = process.env.SHIPPO_TOKEN || ''
    
    // initialize the shippo constructor with token:
    const shippoClient = shippo(shippoToken);
    
    // now you can use shippoClient instead of shippo:
    var addressTo = shippoClient.address.create({
    // <omitted>