I have a simple Fastify backend that sends JSON and a simple React frontend that fetches it. I have cors enabled for my frontend's domain and I have my own API key. I've deployed both seperately on Vercel and each has the correct environment variables on their respective deployments. Why is it giving me this error that it's not valid JSON? ChatGPT seems to think it may also have to do with authentication and I making a GET request on Postman and also got this error:
<!doctype html>
<html lang=en>
<meta charset=utf-8>
<meta name=viewport content="width=device-width,initial-scale=1">
<title>Authentication Required</title>
<style>
...
</style>
<body class=" sso-enabled">
<div>
<div id=card class="card auto-vercel-auth-redirect">
<div class=container><noscript>
<meta http-equiv=refresh
content="1; URL=https://vercel.com/sso-api?url=https%3A%2F%2Fbackend-photo-blog-example.vercel.app%2Fapi%2Fv1%2Fp&nonce=9a456ffe6b4b1a61465630ac0c7180eccd6a1f0cface266d6a7f0c2dd92f76e8">
</noscript>
...
<h1>Authenticating</h1>
</div>
</div>
<div id=bottom-section class="container secondary">
<div class="sso">
<p class=auto-redirect-backup>If you aren't automatically redirected, <a
href="https://vercel.com/sso-api?url=https%3A%2F%2Fbackend-photo-blog-example.vercel.app%2Fapi%2Fv1%2Fp&nonce=9a456ffe6b4b1a61465630ac0c7180eccd6a1f0cface266d6a7f0c2dd92f76e8">click
here</a></p>
</div>...
</div>
<script>
(function(){var form=document.querySelector('form.password');if(form){var button=form.querySelector('button.submit');function onSubmit(e){form.classList.add("submitting");button.disabled=true;}
form.addEventListener('submit',onSubmit);}
function showCheckIconWhenVercelJwtIsSet(){fetch('/.well-known/vercel-user-meta').then((response)=>{if(response.status===200){document.getElementById('spinner').classList.add('dissapear')
document.getElementById('check').classList.add('appear')}else{showCheckIconWhenVercelJwtIsSet()}})}
document.addEventListener("DOMContentLoaded",function(){window.location.href="https://vercel.com/sso-api?url=https%3A%2F%2Fbackend-photo-blog-example.vercel.app%2Fapi%2Fv1%2Fp&nonce=9a456ffe6b4b1a61465630ac0c7180eccd6a1f0cface266d6a7f0c2dd92f76e8";document.getElementById('card').style.animation='none'
document.getElementById('bottom-section').style.animation='none'
showCheckIconWhenVercelJwtIsSet()});})();
</script>
Here's my Fastify app.js, routes, and controllers: app.js
const fastify = require("fastify")({ logger: true });
const mongoose = require("mongoose");
require("dotenv").config();
// cors
const cors = require("@fastify/cors");
fastify.register(cors, {
origin: "https://www.example.com",
});
// routes
const contentRoutes = require("./routes/content.routes.js");
// database
mongoose
.connect(process.env.MONGODB_URI, {
useNewUrlParser: true,
useUnifiedTopology: true,
})
.then(() => console.log("MongoDB connected"))
.catch((err) => console.log(err));
// server
fastify.register(contentRoutes, { prefix: "/api/v1/" });
const start = async () => {
try {
await fastify.listen(5000);
fastify.log.info(`Server listening on ${fastify.server.address().port}`);
} catch (error) {
fastify.log.error(error);
process.exit(1);
}
};
start();
routes.js
const fastify = require("fastify")({ logger: true });
const contentController = require("../controllers/content.controller");
async function auth(request, reply) {
const apiKey = request.headers["x-api-key"];
const knownKey = process.env.API_KEY;
if (apiKey !== knownKey) {
return reply.code(401).send({ error: "Unauthorized" });
}
}
fastify.addHook("preHandler", auth);
async function routes(fastify, options) {
fastify.get("/p", { preHandler: auth }, contentController.get);
fastify.get("/p/:id", { preHandler: auth }, contentController.getId);
fastify.post("/p/create", { preHandler: auth }, contentController.create);
fastify.put("/p/update/:id", { preHandler: auth }, contentController.update);
fastify.delete(
"/p/delete/:id",
{ preHandler: auth },
contentController.deleteId
);
}
module.exports = routes;
controller.js with only the GET request to keep it short
const Content = require("../models/content.model");
async function get(req, reply) {
try {
const content = await Content.find();
reply.send(content);
} catch (error) {
throw new Error(error);
}
}
And finally here is my frontend making the call
import React, { useState, useEffect } from "react";
export default function Photos() {
const [loading, setLoading] = useState(true);
const [posts, setPosts] = useState([]);
useEffect(() => {
fetch(process.env.BACK_URL + "/api/v1/p", {
headers: {
"x-api-key": process.env.API_KEY,
},
})
.then((response) => response.json())
.then((data) => {
setPosts(data);
setLoading(false);
})
.catch((error) => {
console.error("Error fetching data:", error);
setLoading(false);
});
}, []);
return (
<div> ...
{posts.map((post) => (
<div>
{/* Link to each post */}
<div className="relative group h-52 w-auto overflow-hidden px-1 py-1">
<img
src={post.image}
alt={post.title}
className="w-full h-full object-cover group-hover:opacity-50 group-hover:filter group-hover:brightness-50 group-hover:blur-sm"
/>
<div className="absolute inset-0 flex flex-col align-center items-center justify-center opacity-0 group-hover:opacity-100 duration-500">
<h2 className="text-lg text-white font-mono text-center p-4 mb-4">
{post.title.toUpperCase()}
</h2>
<h2 className="text-lg text-white font-mono">
{post.date_created.toString().slice(0, 10)}
</h2>
</div>
</div>
</div>
))}
</div>
</div>
);
}