Next.js Mailchimp Marketing API implementation failing in Production

Viewed 35

I have a form on my page where page visitors can subscribe to a mailing list; it makes use of the Mailchimp Marketing API behind the scenes (I followed Lee Robinson's guide to implement it). It works perfectly in the dev environment, but the same code fails in production.

When a user enters their email and presses the submit button, this error is logged to the console: POST https://www.example.com/api/subscribe 405 I have no idea what the problem is, nor where to start looking, because I can't replicate the error in my dev environment.

Here are my dependencies:

"dependencies": {
    "@mailchimp/mailchimp_marketing": "^3.0.75",
    "dotenv": "^8.2.0",
    "framer-motion": "^2.7.5",
    "globby": "^11.0.0",
    "isomorphic-unfetch": "^3.1.0",
    "next": "^10.2.3",
    "next-compose-plugins": "^2.2.0",
    "next-optimized-images": "^2.5.4",
    "prettier": "^2.1.2",
    "rc-drawer": "^4.1.0",
    "react": "16.13.0",
    "react-custom-scrollbars": "^4.2.1",
    "react-dom": "16.13.0",
    "react-ga": "^3.1.2",
    "react-icons": "^3.10.0",
    "react-modal-video": "^1.2.6",
    "react-multi-carousel": "^2.5.5",
    "react-scroll": "^1.8.0",
    "react-stickynode": "^3.0.3",
    "theme-ui": "^0.3.1",
    "typeface-dm-sans": "^1.1.3"
  },

Here is my pages/api/subscribe.js file:

import mailchimp from '@mailchimp/mailchimp_marketing';

mailchimp.setConfig({
  apiKey: process.env.NEXT_PUBLIC_MAILCHIMP_API_KEY,
  server: process.env.NEXT_PUBLIC_MAILCHIMP_SERVER_PREFIX
});

const listId = process.env.NEXT_PUBLIC_LIST_ID;

const subscribe = async (req, res) => {
  const { email } = req.body;
  
  if (!email) {
    return res.status(400).json({ error: 'Email is required' });
  }

  try {
    await mailchimp.lists.addListMember(listId, {
      email_address: email,
      status: "subscribed"
    });

    return res.status(201).json({ error: '' });
  } catch (error) {
    return res.status(500).json({ error: error.message || error.toString() });
  }
}

export default subscribe;

And here is my subscribe.js component:

/** @jsx jsx */
import { useRef, useState } from 'react';
import fetch from 'isomorphic-unfetch';
import { jsx, Container, Flex, Box, Button, Input, Text, Heading } from 'theme-ui';
import { styles } from './subscribe.styles';

export default function Subscribe() {
  // 1. Create a reference to the input so we can fetch/clear it's value.
  const inputEl = useRef(null);
  // 2. Hold a status in state to handle the response from our API.
  const [status, setStatus] = useState({
    submitted: false,
    submitting: false,
    info: { error: false, msg: null },
  });

  const handleMailChimpResponse = (errorMsg, successMsg) => {
    if (errorMsg) {
      // 4. If there was an error, update the message in state.
      setStatus({
        info: { error: true, msg: errorMsg },
      });

      return;
    }

    // 5. Clear the input value and show a success message.
    setStatus({
      submitted: true,
      submitting: false,
      info: { error: false, msg: successMsg },
    });
    inputEl.current.value = '';
  };

  const subscribe = async (e) => {
    e.preventDefault();
    setStatus((prevStatus) => ({ ...prevStatus, submitting: true }));

    // 3. Send a request to Mailchimp API with the user's email address.
    const res = await fetch('/api/subscribe', {
      body: JSON.stringify({
        email: inputEl.current.value
      }),
      headers: {
        'Content-Type': 'application/json'
      },
      method: 'POST'
    });

    const { error } = await res.json();
    handleMailChimpResponse(
      error,
      'Success!  We will email you the latest updates.'
    );
  };

  return (
    <section id="subscribe">
      <Container>
        <Box sx={styles.contentBox}>
          <Box sx={styles.contentBoxInner}>
            <Heading as="h2" sx={styles.title}>
              Join the Waitlist
            </Heading>
            <form onSubmit={subscribe}>
              <Flex sx={styles.subscribeForm}>
                <label htmlFor="email" sx={{ variant: 'styles.srOnly' }}>
                  Email Address
                </label>
                <Input
                  ref={inputEl}
                  id="email"
                  name="email"
                  type="email"
                  placeholder="Enter your email address"
                  required
                />

                <div>
                  {status.info.error && (
                    <div className="error">Error: {status.info.msg}</div>
                  )}
                  {!status.info.error && status.info.msg && (
                    <div className="success">{status.info.msg}</div>
                  )}
                </div>
                <Button
                  type="submit"
                  disabled={status.submitting}
                  className="subscribe__btn"
                  aria-label="Subscribe"
                >
                  {!status.submitting
                    ? !status.submitted
                      ? 'Notify Me'
                      : 'Submitted'
                    : 'Submitting...'}
                </Button>
              </Flex>
            </form>
          </Box>
        </Box>
      </Container>
    </section>
  );
}

I am using the Digital Ocean App Platform to deploy it, and making environment variables available. Any help would be greatly appreciated.

0 Answers
Related