Fetching with route handler
Answered
Beveren posted this in #help-forum
BeverenOP
I am having a hard time understanding the documentation on how to do fetching properly.
What am I doing wrong here and what should be improved in my example?
I want to be able to run it locally as well as on Vercel platform.
Here is what I do so far:
page.tsx:
getRooms function used by page.tsx:
route.ts (/api/rooms/route.ts):
What am I doing wrong here and what should be improved in my example?
I want to be able to run it locally as well as on Vercel platform.
Here is what I do so far:
page.tsx:
import { getRooms } from '@/lib/getRooms';
const Home = async () => {
const rooms = await getRooms();getRooms function used by page.tsx:
export const getRooms = async (): Promise<Rooms> => {
const res = await fetch('/api/rooms');
if (!res.ok) {
// This will activate the closest `error.js` Error Boundary
throw new Error('Failed to fetch rooms');
}
return res.json();
};route.ts (/api/rooms/route.ts):
export async function GET(req: NextRequest) {
// eslint-disable-next-line no-console
console.log('req', req);
try {
const session = await getServerSession(authOptions);
if (!session) {
return new Response('Unauthorized', { status: 403 });
}
const res = await fetch(
'https://somevalidendpoint.azurewebsites.net/rooms',
{
method: 'GET',
next: {
revalidate: 10,
},
headers: {
'Content-Type': 'application/json',
// 'API-Key': process.env.AZURE_AD_CLIENT_ID,
},
}
);
const data = await res.json();
// eslint-disable-next-line no-console
console.log('GET', data);
if (!res.ok) {
// This will activate the closest `error.js` Error Boundary
throw new Error('Failed to fetch rooms');
}
return NextResponse.json(data);
} catch (error) {
return new NextResponse(null, { status: 500 });
}
}Answered by Beveren
I wrapped this around, seems to work now:
<Suspense>
<CurrentUser />
</Suspense>40 Replies
and is there an issue so far?
BeverenOP
Internal error: TypeError: Failed to parse URL from /api/rooms
ahhh i see the issue
BeverenOP
I have been reading this, but dont get it
how do I use the api route in page?
like as its a server component, you don't need a fetch request, you can just do the same code but inside the component
BeverenOP
where dont i need fetch request?
what file
so, effectivly make the code inside
GET a function like getRooms and use it accordingly... (but just the part that fetches from azure and does parsing)BeverenOP
but it has a try catch
so then i would need to wrap the whole try catch inside a function called getRooms?
just do the same code basicly, as you don't actually use
reqBeverenOP
I cannot export inside the export
how do i use it?
page.tsx:
getRooms function used by page.tsx:
route.ts (/api/rooms/route.ts):
import { getRooms } from '@/lib/getRooms';
const Home = async () => {
const rooms = await getRooms();getRooms function used by page.tsx:
export const getRooms = async (): Promise<Rooms> => {
try {
const session = await getServerSession(authOptions);
if (!session) {
throw new Error("Unauthorized")
}
const res = await fetch(
'https://somevalidendpoint.azurewebsites.net/rooms',
{
method: 'GET',
next: {
revalidate: 10,
},
headers: {
'Content-Type': 'application/json',
// 'API-Key': process.env.AZURE_AD_CLIENT_ID,
},
}
);
const data = await res.json();
// eslint-disable-next-line no-console
console.log('GET', data);
if (!res.ok) {
// This will activate the closest `error.js` Error Boundary
throw new Error('Failed to fetch rooms');
}
return data
} catch (error) {
throw new Error('Failed to fetch rooms');
}
};route.ts (/api/rooms/route.ts):
export async function GET(req: NextRequest) {
// eslint-disable-next-line no-console
console.log('req', req);
try {
const data = getRooms()
return NextResponse.json(data);
} catch (error) {
return new NextResponse(null, { status: 500 });
}
}can you do something like this (i haven't tested... just asuming based on your code)
you don't need to keep the api route, but you can (idk if you are using it elsewhere or just want it)
BeverenOP
I have two calls, 1 Get to fetch rooms, and 1 Post to book meeting rooms also it has ad user session to validate users, I also need to provide some token in request to azure API at some point.
And the post is client side since i need a body payload from the user, but the get is running in two page.ts files that are server side, post is client side though
the issue is that page.ts is a server component, which means that everything is done on the server and then the code is given (so the fetch reqs are done server side)
BeverenOP
since the post request made client side needs to provide user token to back-end i need to provide it somehow by cookie or something, not sure how NextAuth handles this
and i want it to be secure
you get the same auth stuff in server components as api routes
but if you really want to to that request client side, you can do the fetch and content in a client component (make new file and put
'use client' at the top)BeverenOP
In your example, how can I trigger the route?
is routes only triggered client side?
yeah
BeverenOP
ahhh
routes is only if you want to do fetch from client
otherwise it would use server rendered version
BeverenOP
got it, thought it was one fit all
let me know when you try it
and if you have any issues
BeverenOP
sure, gonna test now
BeverenOP
Well it worked somewhat, but I have issues with pages being deopted:
/components/Layout/Header/index.tsx
'use client';
import Link from 'next/link';
import Logo from '../Logo';
import CurrentUser from './CurrentUser';
import ThemeChanger from '../../Theme/ThemeChanger';
const Header = () => (
<div className="m-4 grid grid-cols-2 gap-4">
<div>
<Link href="/">
<Logo
className="fill-darkgrey dark:fill-pebble"
title="Knowit"
/>
</Link>
</div>
<div className="text-right">
<CurrentUser /> <-- Culprit
<ThemeChanger />
</div>
</div>
);
export default Header;BeverenOP
'use client';
import { PiUserCircleDuotone } from 'react-icons/pi';
import { useSearchParams } from 'next/navigation';
import { useSession, signIn, signOut } from 'next-auth/react';
import { Button, Popover } from '@mui/material';
import { useState, MouseEvent } from 'react';
const CurrentUser = () => {
const { data: session } = useSession();
const searchParams = useSearchParams();
const callbackUrl = searchParams.get('callbackUrl') ?? undefined;
const [anchorEl, setAnchorEl] = useState<HTMLButtonElement | null>(null);
const handleClick = (event: MouseEvent<HTMLButtonElement>) => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
const open = Boolean(anchorEl);
const id = open ? 'simple-popover' : undefined;
return (
<>
<button
type="button"
aria-describedby={id}
onClick={handleClick}
>
<PiUserCircleDuotone className="h-8 w-8 text-darkgrey dark:text-pebble" />
</button>
<Popover
id={id}
open={open}
anchorEl={anchorEl}
onClose={handleClose}
anchorOrigin={{
vertical: 'bottom',
horizontal: 'left',
}}
>
<div className="p-4">
{session ? (
<Button onClick={() => signOut()}>
Log out
</Button>
) : (
<Button
onClick={() => signIn('azure-ad', { callbackUrl })}
>
Login
</Button>
)}
</div>
</Popover>
</>
);
};
export default CurrentUser;So why is the CurrentUser trowing off the whole app? Should this be an issue while using 'client side' ?
BeverenOP
I wrapped this around, seems to work now:
<Suspense>
<CurrentUser />
</Suspense>Answer