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.
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>