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.
From the code you shared, it looks like you are not initializing the
shippomodule correctly inShippo.js, thereforeshippo.createmethod does not exist.In your code, you are importing the
shippomodule, but not initializing it with the key:Note that
shippoTokenremains unused in your file.In the Shippo library documentation, they have the following example:
Since you are using ES modules, you can't use this one-liner, so try something like this instead: