Search code examples
typescriptnext.jsnext.js14

Nextjs API Integration not working, error 404


Really suffering here and I do not know wherelse to go. I have been for days trying to integrate my local nextjs 14 app to the CVENT API, but no matter what I always get a 404 error, and in the frontend console:

HERE the CVENT doc: https://developers-eur.cvent.com/documentation

[Fast Refresh] rebuilding page.tsx:12 GET http://localhost:3000/api/cvent-api 404 (Not Found) eval @ page.tsx:12 Show 94 more frames page.tsx:29 API error: Error: HTTP error! status: 404 at eval (page.tsx:15:17) eval @ page.tsx:29 Promise.catch (async)
eval @ page.tsx:28 Show 97 more frames

.env.local:

# For server-side
CVENT_CLIENT_ID="hereGOEStheID"
CVENT_CLIENT_SECRET="heGoestheSecretID"
CVENT_SCOPE=event/events:read
NEXT_PUBLIC_API_HOSTNAME="https://api-platform-eur.cvent.com"

Api/cvent-api.ts:

import type { NextApiRequest, NextApiResponse } from 'next';
import fetch from 'node-fetch'

const baseUrl = 'https://api-platform-eur.cvent.com';

async function getAccessToken(): Promise<string> {
  const encodedCredentials = Buffer.from(`${process.env.CVENT_CLIENT_ID}:${process.env.CVENT_CLIENT_SECRET}`).toString('base64');
  const authHeader = `Basic ${encodedCredentials}`;

  // Ensure that client_id is defined or provide a fallback empty string to avoid undefined values
  const clientId = process.env.CVENT_CLIENT_ID || '';

  // Correctly format data for POST request
  const params = new URLSearchParams({
    grant_type: 'client_credentials',
    client_id: clientId,
  });

  const response = await fetch(`${baseUrl}/ea/oauth2/token`, { // Use base URL without appending client_id or scopes in the URL
    method: 'POST',
    headers: {
      Authorization: authHeader,
      'Content-Type': 'application/x-www-form-urlencoded',
    },
    body: params.toString(),
  });

  if (!response.ok) {
    throw new Error(`Error fetching access token, status: ${response.status}`);
  }

  const data = await response.json();
  return data.access_token;
}

// Handler function for the API route
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
  try {
    // Obtain a new access token
    const accessToken = await getAccessToken();
    console.log(`access token is: ${accessToken}`)

    // Make API call to Cvent to get attendees list
    const response = await fetch(`${baseUrl}/ea/attendees`, {
      method: "GET",
      headers: {
        "Content-type": "application/json",
        Authorization: `Bearer ${accessToken}`,
      }
    });

    if (!response.ok) {
      throw new Error(`Error fetching attendees list, status: ${response.status}`);
    }

    const data = await response.json();
    console.log('Data is:', data);
    res.status(200).json(data);
  } catch (error) {
    console.error(error);
    res.status(500).json({ message: 'Internal Server Error' });
  }
}

page.tsx:

"use client";
import React, { useState, useEffect } from "react";

const IndexPage = () => {
  const [connectionStatus, setConnectionStatus] = useState<string | null>(null);
  const [data, setData] = useState<any[]>([]); // Adjust the type based on your response structure
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);

  useEffect(() => {
    setIsLoading(true);
    fetch("/api/cvent-api") // Corrected URL
      .then((response) => {
        if (!response.ok) {
          throw new Error(`HTTP error! status: ${response.status}`);
        }
        return response.json();
      })
      .then((responseData) => {
        setData(responseData);
        console.log("API response:", responseData);
        if (responseData && responseData.length > 0) {
          setConnectionStatus("Connection successful and attendees fetched!");
        } else {
          setConnectionStatus("Connection successful but no attendees found.");
        }
      })
      .catch((error) => {
        console.error("API error:", error);
        setError(error.toString());
        setConnectionStatus(`Failed to fetch: ${error.toString()}`);
      })
      .finally(() => setIsLoading(false));
  }, []);

  return (
    <div>
      <h1>Connection Status</h1>
      {isLoading && <p>Loading...</p>}
      {error && <p>{error}</p>}
      {connectionStatus && <p>{connectionStatus}</p>}
      {data && data.length > 0 && (
        <ul>
          {/* Adjust the rendering logic based on your response data */}
          {data.map((attendee) => (
            <li key={attendee.id}>{attendee.name}</li>
          ))}
        </ul>
      )}
    </div>
  );
};

export default IndexPage

Solution

  • It looks like you are using the app router (you mentioned v14 and you have a "page.tsx") but the API handler you exposed is a pages router API handler.

    In order to expose an API endpoint using the app router, you need to follow a different convention. In your case, try creating a file at app/api/cvent-api/route.ts. In this file, expose a function named GET that accepts an argument of type Request.

    See https://nextjs.org/docs/app/building-your-application/routing/route-handlers