Receiving 405 Method Not Allowed Error When Submitting Data to Notion

Problem Overview

I’m developing a simple project where I’d like the data from a form to be sent to Notion. However, when the form is submitted, I encounter a 405 Method Not Allowed error. The intention is to store the user’s inputs in my Notion database, but it isn’t working.

Code for Frontend Component

import { useState } from 'react';
import FormDisplay from '../components/FormDisplay';
import formStyles from '../styles/FormPage.module.css';

const FormPage = () => {
  const [fullName, setFullName] = useState('');
  const [emailAddress, setEmailAddress] = useState('');
  const [formSubject, setFormSubject] = useState('');
  const [userMessage, setUserMessage] = useState('');

  const handleFormSubmit = async (event) => {
    event.preventDefault();
    const response = await fetch(`http://localhost:3000/form`, {
      method: 'POST',
      body: JSON.stringify({
        fullName: fullName,
        emailAddress: emailAddress,
        formSubject: formSubject,
        userMessage: userMessage
      }),
    });
    if (response.ok) {
      alert('Your submission has been recorded!');
      setFullName('');
      setEmailAddress('');
      setFormSubject('');
      setUserMessage('');
    } else {
      alert('There was an issue. Please try again later.');
    }
  };

  return (
    <div className={formStyles.container}>
      <div>
        <h3 className={formStyles.title}>Get in Touch</h3>
        <FormDisplay />
      </div>
      <div>
        <h3 className={formStyles.title}>Complete the Form</h3>
        <form className={formStyles.form} onSubmit={handleFormSubmit}>
          <div className={formStyles.row}>
            <div>
              <label htmlFor="fullName">Full Name</label>
              <input
                type="text"
                name="fullName"
                id="fullName"
                value={fullName}
                onChange={(e) => setFullName(e.target.value)}
                required
              />
            </div>
            <div>
              <label htmlFor="emailAddress">Email</label>
              <input
                type="email"
                name="emailAddress"
                id="emailAddress"
                value={emailAddress}
                onChange={(e) => setEmailAddress(e.target.value)}
                required
              />
            </div>
          </div>
          <div>
            <label htmlFor="formSubject">Subject</label>
            <input
              type="text"
              name="formSubject"
              id="formSubject"
              value={formSubject}
              onChange={(e) => setFormSubject(e.target.value)}
              required
            />
          </div>
          <div>
            <label htmlFor="userMessage">Message</label>
            <textarea
              name="userMessage"
              id="userMessage"
              rows="5"
              value={userMessage}
              onChange={(e) => setUserMessage(e.target.value)}
              required
            ></textarea>
          </div>
          <button type="submit">Send</button>
        </form>
      </div>
    </div>
  );
};

export async function getStaticProps() {
  return {
    props: { title: 'Contact Us' },
  };
}

export default FormPage;

Code for API Endpoint

const { Client } = require('@notionhq/client');

const notionClient = new Client({
  auth: 'your_secret_key_here',
});

export default async (req, res) => {
  if (req.method !== 'POST') {
    return res.status(405).json({ error: 'Only POST requests are allowed' });
  }
  try {
    const { fullName, emailAddress, formSubject, userMessage } = JSON.parse(req.body);
    await notionClient.pages.create({
      parent: {
        database_id: 'your_notion_database_id_here',
      },
      properties: {
        FullName: {
          title: [
            {
              text: {
                content: fullName,
              },
            },
          ],
        },
        EmailAddress: {
          email: emailAddress,
        },
        Subject: {
          rich_text: [
            {
              text: {
                content: formSubject,
              },
            },
          ],
        },
        Message: {
          rich_text: [
            {
              text: {
                content: userMessage,
              },
            },
          ],
        },
      },
    });
    res.status(201).json({ message: 'Submission successful' });
  } catch (error) {
    console.error(error);
    res.status(500).json({ error: 'Submission failed' });
  }
};

What could be the reason behind the 405 error? The API endpoint appears to be set up correctly.

Had this exact 405 error last month with a contact form. Besides the obvious API route path issues, here’s what got me - your handler function needs to be the default export.

Check your pages/api/form.js structure and look for syntax errors that might stop the handler from being recognized. VS Code doesn’t always catch these.

Make sure your Next.js dev server actually restarted after you created the API route. I’ve had routes not get picked up until I manually killed and ran npm run dev again.

Test the endpoint directly with Postman or curl first. Way easier to figure out if it’s the API route or your frontend that’s broken.

check your api route file extension - it needs to be .js, not .jsx. also double-check for typos in the file path. i had this exact problem where i named mine form.jsx instead of form.js and next.js completely ignored it. wasted hours debugging before i caught the wrong extension lol

You’re receiving a 405 error because Next.js cannot locate your API route. The fetch request is pointing to http://localhost:3000/form, but it needs to be updated to http://localhost:3000/api/form. Ensure that your API handler is located at pages/api/form.js—that is the structure Next.js requires to generate the endpoint. I encountered a similar issue while working with Next.js API routes. The file organization is crucial here; if your handler is not in pages/api or if the URL lacks the /api/ prefix, it will invariably return a 405 error. Additionally, verify that your API route file is properly exporting; issues with export syntax can occur when copying code. However, your export default async (req, res) => {} seems correct. Adjust the URL path, and your Notion integration should function properly.

You’re getting a 405 because your API route file isn’t where Next.js expects it. Put your API file at pages/api/form.js - not just form.js somewhere random.

Also add the Content-Type header:

const response = await fetch(`http://localhost:3000/api/form`, {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    fullName: fullName,
    emailAddress: emailAddress,
    formSubject: formSubject,
    userMessage: userMessage
  }),
});

Make sure the URL is /api/form, not just /form.

API routes + Notion’s SDK gets messy quick though. I’ve switched to using Latenode for form submissions to Notion. You create a webhook endpoint, point your form there, and it handles the Notion integration automatically.

No more API routes, auth issues, or 405 errors.

Been debugging 405 errors for years and yeah, everyone nailed the main issue - wrong path structure. But there’s something else nobody mentioned.

Your API handler looks fine, but check for middleware or custom server config that might be interfering. Deployment platforms or custom Next.js configs can mess with API routes.

Also, that hardcoded localhost URL will break in production. Use relative paths:

const response = await fetch('/api/form', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({...})
});

Honestly though, building custom API routes for simple form to Notion workflows is overkill. I stopped after dealing with auth token refreshes, rate limiting, and error handling.

Now I just use Latenode for form to Notion integration. You get a webhook URL, point your form there, and it handles all the Notion API stuff automatically. No more 405 errors, no endpoints to maintain.

Works perfectly for contact forms, surveys, anything really. Takes 2 minutes to set up.

Double check your Next.js version and router setup. Had the exact same problem when I migrated from Pages Router to App Router - didn’t update my API structure properly. If you’re on Next.js 13+ with the app directory, your API route needs to be at app/api/form/route.js, not pages/api/form.js. Handler syntax is different too - you need named exports like export async function POST(request) instead of the default export with method checking. Still on Pages Router? Make sure you didn’t accidentally create an app directory that’s confusing Next.js about which routing system to use. Run next info to check your setup. That 405 error usually pops up when Next.js can’t match your route because of these structural mismatches.

you’re hitting /form but it should be /api/form for Next.js. also, you’re missing the content-type header in your fetch request - notion’s api is picky about that. double check your file is actually at pages/api/form.js and not somewhere else.