skip to Main Content

I’m trying to create an API and fetch data in Next JS 13. I’ve read this question: How do you put api routes in the new app folder of Next.js?

and came up with these:

/src/app/hooks/useLocation.ts:

export const getUserLocation = () => {
  return new Promise((resolve, reject) => {
    if ("geolocation" in navigator) {
      navigator.geolocation.getCurrentPosition(({coords}) => {
        const {latitude, longitude} = coords;
        resolve({latitude, longitude});
      }, (error) => {
        reject(error);
      });
    } else {
      reject("Geolocation not available");
    }
  });
};

/src/app/location/route.ts:

import {getUserLocation} from "@/app/hooks/useLocation";
import {NextResponse} from "next/server";

export async function GET() {
  const userLocation = getUserLocation();
  return NextResponse.json(userLocation);
}

And /src/app/layout.tsx:

'use server';
import './globals.scss'

interface LayoutProps {
  children: React.ReactNode;
}
export default async function RootLayout({ children }: LayoutProps) {
  try {
    const response = await fetch('/api/location', {
      method: 'GET',
      cache: 'no-store',
      headers: {
        'Content-Type': 'application/json'
      }
    });

    if (!response.ok) {
      throw new Error('Response error');
    };

    const responseData = await response.json();
    console.log(responseData);
    return responseData;
  } catch (error) {
    console.error('Error fetching data:', error);
  };

  return (
    <html lang="en">
    <body>
    <main className="flex min-h-screen flex-col items-center justify-between p-24 main">
      {children}
    </main>
    </body>
    </html>
  )
};

However when running, I got error:

Error fetching data: TypeError: Failed to parse URL from /api/location
    at new Request (node:internal/deps/undici/undici:7004:19)
    at T (C:projectsxiaohan-dunode_modulesnextdistcompilednext-serverapp-page-experimental.runtime.dev.js:34:233400)
    ... 13 lines matching cause stack trace ...
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5) {
  [cause]: TypeError [ERR_INVALID_URL]: Invalid URL

Any idea what I’m doing wrong here?

2

Answers


  1. I think you should use the whole URL instead of /api/location.

    Maybe store the route link in an env and use ${API_URL}/api/location

    Login or Signup to reply.
  2. There are few reasons why it won’t work properly,

    1. As the above user said, your API route /src/app/location/route.ts isn’t proper, keep your API in src/app/api/folderNameAsAPIName/route.js. Make api folder & then make a folder inside it with API Name & inside it a route.js/ts file. Read Here Route Handlers https://nextjs.org/docs/app/building-your-application/routing/route-handlers. This is probably the reason why it gives error Error fetching data: TypeError: Failed to parse URL from /api/location TypeError [ERR_INVALID_URL]: Invalid URL as there is no api folder in src/app.
    2. Geolocation API & Navigator API are client side(it runs in browser) you have called it in /api folder which is server-side, hence it wont run. (it will show Error ReferenceError: navigator is not defined). Read More Navigator: geolocation property
    3. 'use client' to do client side operations like events you should add ‘use client’ at top of that component & import it in page. That component will be rendered client side. Read More https://nextjs.org/docs/app/building-your-application/rendering/client-components
    4. Please read this part, Benefits of Client Rendering https://nextjs.org/docs/app/building-your-application/rendering/client-components#benefits-of-client-rendering

    Here’s a small code that I made (it’s in .js you may re-write it in .ts):

    Folder Structure :

    projectName
    ├── .gitignore
    ├── jsconfig.json
    ├── next.config.js
    ├── package-lock.json
    ├── package.json
    ├── postcss.config.js
    ├── public
    │   ├── next.svg
    │   └── vercel.svg
    ├── README.md
    ├── src
    │   └── app
    │       ├── api
    │       │   └── location
    │       │       └── route.js
    │       ├── comp
    │       │   └── Loc.js
    │       ├── favicon.ico
    │       ├── globals.css
    │       ├── hooks
    │       │   └── useLocation.js
    │       ├── layout.js
    │       └── page.js
    └── tailwind.config.js
    

    comp is component folder.

    1. useLocation hook remains as it is in its current folder.
    2. Then under src/app/api/ I made a folder location & inside it a file route.js.

    This Location API will return Location API says Hello !! & when you uncomment those 2 lines above, you will see error :

    Code inside src/app/api/location/route.js

    import { getUserLocation } from "@/app/hooks/useLocation";
    import { NextResponse } from "next/server";
    
    export async function GET() {
    
        console.log("Location API was HIT !");
    
        // let location = await getUserLocation()
        // console.log(location);
    
        // UN COMMENT THE ABOVE TO SEE THE ERROR
    
        return NextResponse.json({ data: "Location API says Hello !!" });
    }
    

    Then i made a component inside which i will get Location using that hook & also i made a call to our location api :

    Loc Component code :

    Note : the api url is /api/location is correct as per NextJS convention.

    'use client'
    import React, { useEffect, useState } from 'react'
    import { getUserLocation } from '../hooks/useLocation';
    
    const Loc = () => {
    
        const [Data, SetData] = useState(null)
    
        async function GetLoc() {
            let response = await getUserLocation()
            console.log("Location Hook response : ", response);
    
        }
    
        async function CallHello() {
            try {
                let resp = await fetch('/api/location', {
                    method: 'GET',
                    cache: 'no-store',
                    headers: {
                        'Content-Type': 'application/json'
                    }
                })
    
                let { data } = await resp.json()
                // IMPORTANT TO CONVERT TO JSON
    
                console.log("Recieved from API : ", data);
                SetData(data)
    
    
            } catch (e) {
    
                throw new Error("Error occured while calling Location API")
            }
        }
    
        useEffect(() => {
            GetLoc()
            CallHello()
        }, [])
        return (
            <div>
                Data from Location API : {Data}
            </div>
        )
    }
    
    export default Loc
    

    Now just import this <Loc /> component in your page.js to see it run.

    Output:

    In browser :

    1. Popup for location
    2. In Console :

    Recieved from API : Location API says Hello !! Location Hook response : {latitude: someNumbers, longitude:someNumbers } Location Hook response : {latitude:someNumbers , longitude:someNumbers }

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search