I'm currently facing a major issue and I've hit a dead end. I've been spending days trying to connect my local nextjs 14 app to the CVENT API, but I keep receiving a persistent 404 error. Here's what is displayed in the frontend console:
Here is the link to the CVENT 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