Next.js Discord

Discord Forum

How to use API Router to pass off-site data

Answered
Cordilleran Flycatcher posted this in #help-forum
Open in Discord
Cordilleran FlycatcherOP
Hello,
I'm building a simple kiosk style webpage for a project. I have mapbox gl running on a client-side page with "use client".

ON that page, I cannot make any requests to the api.rsoe-edis.org page that I need to make, however I can get the data needed on a different file.

I have my src/api/rsoe.tsx file set up,
import { type NextApiRequest, type NextApiResponse} from "next";
import { isNull } from "util";
import { NextRequest, NextResponse } from "next/server";

type ResponseData = {
    data: any
}

export default async function passData(
    req: NextApiRequest,
    res: NextApiResponse, 
    eventlist: number){    

    let cat;
    if(Category[eventlist]!==null)
    {
        cat = Category[eventlist];
    }
        

    const output_data = await GoFetch(cat);// This builds my url based on the category list so I can change which group of info I'm fetching. Works great for what it needs to do!

    return res.status(200).json(output_data)
}

Now, I have my mapbox page that runs and builds my mapbox map. (See comment for script)

I have a function that tries to GET from my weather api in order to grab that weather data and get it to the client. However, it returns a 404 error. What am I missing or need to get my internal API to get data from an External API?
Answered by Cordilleran Flycatcher
export async function GET(req: NextRequest)
{
   // const check = await fetch(get_category(Category.Tsunami));
    const check = await GoFetch(Category.Tsunami);// <---
    //Category.X not Category[index]
    return NextResponse.json(check);
}
View full answer

100 Replies

Cordilleran FlycatcherOP
This is the other side of the script

"use client"
import * as React from "react";
import mapboxgl, { Map } from "mapbox-gl";
import "mapbox-gl/dist/mapbox-gl.css";
import {Category, /*passData*/} from './rsoe/rsoe';

const MapboxMap = (/*{jsondata: data}*/) =>
{
    const [map, setMap] = React.useState<mapboxgl.Map>();

    const mapNode = React.useRef(null);
    mapboxgl.accessToken = <KEY>; // Just hiding my key
    React.useEffect(()=>{
        const node = mapNode.current;

        if(typeof window === "undefined" || node === null) return;

        const mapboxMap = new mapboxgl.Map({
            container: node,
            style: "mapbox://styles/mapbox/light-v11",
            center: [-72,41],
            zoom:0.6
        });
        setMap(mapboxMap);
        const marker = new mapboxgl.Marker().setLngLat([-72,40]).addTo(mapboxMap);
        
        async function grab() // Returns 404 error
        {
           const res = await fetch('/api/rsoe',{
            method: 'GET',
            headers: {,
            mode:"cors"
           });
           console.log(res.json());
        }
        grab();
        // END
        return ()=>{mapboxMap.remove()};
    },[]);
    return <div ref={mapNode} style={{width:"100%",height:"100%"}}/>;
}
export default MapboxMap
Pacific herring
@Cordilleran Flycatcher
are you there
when you fetch
remove the dot
'./api/rsoe'
to
'/api/rsoe'
try
Cordilleran FlycatcherOP
Thanks for the suggestion! I did clean that up, noticed it while debugging some yesterday. Unfortunately it didn't help.

I've set up a get function,
export async function GET(req: NextRequest)
{
   
    return NextResponse.json({message: "test"});
}

But the response is 404 when trying to find api/rsoe
I can add a route.tsx to the folder and recive 405 instead of 404.
try route.ts
@riský try `route.ts`
Pacific herring
dont have to
@Pacific herring dont have to
wdym, .tsx is surly going to yield weirdness when it expects just ts (not react in route handler)
Pacific herring
he's using app folder features in a project that uses page router features
this is what you need to look for
yeah, that is a diferent problem, but i was just commenting on the tsx part (it wasn't clear which one he was using)
Pacific herring
when you look at the doc, make sure you are checking the right version
Cordilleran FlycatcherOP
I am using App, not page
where is your rsoe file
^ and include the full path (ie with app/)
Pacific herring
oh 🤦 now i realized, got confused as fuck
doo what coffee said
about the route.ts
@riský try `route.ts`
Pacific herring
^
this
Cordilleran FlycatcherOP
So I did route.tsx and was getting a 405, adjust the script to match my get correctly and got to 500 error
did you make it route.ts (without the x)?
Cordilleran FlycatcherOP
same output
and what was the error that you had (ie in console)
Cordilleran FlycatcherOP
mapbox.tsx:43


GET http://localhost:3000/api/rsoe net::ERR_ABORTED 500 (Internal Server Error)
line 43 being
what is your mapbox.tsx file's code
Cordilleran FlycatcherOP
async function grab() // 41
        { //42
           const res = await fetch('api/rsoe',{ //43
            method: 'GET',
            headers: {
                //'Content-Type': "applications/json" 
                //'Access-Control-Allow-Origin':'*'               
            },
            //mode:"cors"
           });
           console.log(res);
        }
        grab();
and that is client or server comp
Cordilleran FlycatcherOP
I just tried commenting out the headers and mode as well
client
and you don't have any errors in your console (where you run npm start/dev)?
Cordilleran FlycatcherOP
so in my src/app folder I have /api/rsoe/
In that directory is route.tsx which is returning 500 errors
And rsoe.tsx which when calling that folder.

I've just seen in .next / app /api/rsoe/route.ts shows up.
>
[{
"resource": "/u:/Projects/GlobalEvents-NextJS/global-events/.next/types/app/api/rsoe/route.ts",
"owner": "typescript",
"code": "2344",
"severity": 8,
"message": "Type '{ tag: "GET"; return_type: Promise<typeof NextResponse>; }' does not satisfy the constraint '{ tag: "GET"; return_type: void | Response | Promise<void | Response>; }'.\n Types of property 'return_type' are incompatible.\n Type 'Promise<typeof NextResponse>' is not assignable to type 'void | Response | Promise<void | Response>'.\n Type 'Promise<typeof NextResponse>' is not assignable to type 'Promise<void | Response>'.\n Type 'typeof NextResponse' is not assignable to type 'void | Response'.\n Type 'typeof NextResponse' is missing the following properties from type 'Response': headers, ok, redirected, status, and 10 more.",
"source": "ts",
"startLineNumber": 59,
"startColumn": 7,
"endLineNumber": 62,
"endColumn": 8
}]
Also, thank you both for helping out!
well, i still want you to use .ts first
but the type error is very intresting
Cordilleran FlycatcherOP
added
.then(r=>console.log(r));
to the end of my fetch in grab() but no change
Cordilleran FlycatcherOP
mapbox.tsx
"use client"
import * as React from "react";
import mapboxgl, { Map } from "mapbox-gl";
import "mapbox-gl/dist/mapbox-gl.css";
import {Category, /*passData*/} from './rsoe/rsoe';

export default function MapboxMap()
{
    const [map, setMap] = React.useState<mapboxgl.Map>();

    const mapNode = React.useRef(null);
    mapboxgl.accessToken = "pk.eyJ1IjoiY2djdHNjaWVuY2VjZW50ZXIiLCJhIjoiY2xpM2dld3AzMGJkbTNlcGZ2aHowajZncCJ9.vFc7nroP3Pa0Cg7KAxvZjw"
    React.useEffect(()=>{

        //////////////////////////////
        //   Set up my Map          //
        //////////////////////////////

        const node = mapNode.current;

        if(typeof window === "undefined" || node === null) return;

        const mapboxMap = new mapboxgl.Map({
            container: node,
            style: "mapbox://styles/mapbox/light-v11",
            center: [-72,41],
            zoom:0.6
        });
        setMap(mapboxMap);
        
        
        const marker = new mapboxgl.Marker().setLngLat([-72,40]).addTo(mapboxMap);
        
        async function grab()
        {
           const res = await fetch('api/rsoe',{
            method: 'GET',
            headers: {
                'Content-Type': "applications/json" 
                //'Access-Control-Allow-Origin':'*'               
            },
            //mode:"cors"
           }).then(r=>console.log(r));
           //console.log(res);
        }
        grab();
        
        // END
        return ()=>{mapboxMap.remove()};
    },[]);
    return <div ref={mapNode} style={{width:"100%",height:"100%"}}/>;
}


function AddLayer(m: Map, layerName: any)
{

}
rsoe.ts
import { type } from "os";




const site = "https://api.rsoe-edis.org/"
let events = "event/Events";
let key = process.env.RSOE_TOKEN;
export let Category = Object.create(null, { // Add more values or remove values for more categories.
    Tsunami: {value: ["HY","TSU"], enumerable:true},
    Earthquak:{value: ["GE","ERQ"], enumerable:true},
    Cyclone:{value:["WE","CYC"], enumerable:true},
    Volcano:{value:["GE","VOE"], enumerable:true},
    ExtremeRain:{value:["WE","EXR"], enumerable:true},
    SevereWeather:{value:["WE","SEW"], enumerable:true},
    Tornado:{value:["WE","TOR"], enumerable:true}

});


////////////////////////////////////////////
export async function RSOE_Fetch(url="",data={})
{
    console.log("DEBUG FETCH");
    const response = await fetch(url,data);
    
    return response.json();
}

export function get_category(cat: string[2])
{
    let url=site+events+"?apiKey="+key;
    url+="&category="+cat[0]+"&subCategory="+cat[1];

    return url;
}

export async function GoFetch(c: string[2])
{
    return RSOE_Fetch(get_category(c),{});
}

export async function SaveWeatherData()
{
    
    for(var c in Category)
    {
        //console.log(Category[c]);
        GoFetch(Category[c]).then((result)=>
        {
            var data = result;
            console.log(typeof(data));
            
            var date = new Date();
            var today = date.getFullYear() + "-"+(date.getMonth()+1)+"-"+(date.getDate()+1);
            
            /// Saving Variables
            
           
            result.writeFile(today+".json",)
        });
    }
}
////////////////////////////////////////////

Part 1
you may want to not share the accessToken, but idk
Cordilleran FlycatcherOP
////////////////////////////////////////////
///// CROSS API FUNCTIONS //////

import { type NextApiRequest, type NextApiResponse} from "next";
import { isNull } from "util";
import { NextRequest, NextResponse } from "next/server";

type ResponseData = {
    data: any
}

export default async function passData(
    req: NextApiRequest,
    res: NextApiResponse, 
    eventlist: number){    

    let cat;
    if(Category[eventlist]!==null)
    {
        cat = Category[eventlist];
    }
        

    const output_data = await GoFetch(cat);

    //return res.status(200).json(output_data)
    return NextResponse.json({message:"Test"})
}


///////////////////////////////////////////////////////////////////////

/*
export async function GET(req: NextRequest){
    try{
            const response = await fetch('https://api.rsoe-edis.org/event/Events?apiKey=UMC44NTk4NDEwOTEwOTI2NzYyMDIzLTA0LTEzIDE5OjQ1OjAxLjAyNjYyNyswMA');
            const data = await response.json();
            return NextResponse.json(data);    
    }catch(error)
    {
            console.log(error);
            return NextResponse.json(error);
    }
}*/

export async function GET(req: NextRequest)
{
   
    return NextResponse.json({message: "test"});
}
part2
ahh can you try removing the export default
Cordilleran FlycatcherOP
on passData?
ye
Cordilleran FlycatcherOP
Oh sorry forgot to put Route.ts up
my route ts*
import { NextRequest, NextResponse } from "next/server";
import passData, { Category, /*GET_TEST, */GoFetch } from "./rsoe";

export async function GET(req: NextRequest)
{
    const res = NextResponse;
    res.json({message:"test"})
    return res;
}
Cordilleran FlycatcherOP
It did not
same error?
Cordilleran FlycatcherOP
yeah
wait maybe return in one line and not do your weird const res
return NextResponse.json({message:"test"})
Cordilleran FlycatcherOP
export async function GET(req: NextRequest)
{
   /* const res = NextResponse;
    res.json({message:"test"})*/
    return NextResponse.json({message:'test'});
}
That worked!
I got 200 🙂
export async function GET(req: NextRequest)
{
    const res = {message:"Test 2"};
    
    return NextResponse.json(res);
}
returns....
so, your question is solved?
Cordilleran FlycatcherOP
Maybe. Depends on if I can get the json data through
Cordilleran FlycatcherOP
Moment of truth.
export async function GET(req: NextRequest)
{
    const res = await GoFetch(Category[0]);
    
    return NextResponse.json(res);
}
500 error
VM14016:1 Uncaught (in promise) SyntaxError: Unexpected end of JSON input at grab (webpack-internal:///(:3000/app-pages-browser)/./src/app/api/mapbox.tsx:48:48)
just make sure res returning object in route handler
Cordilleran FlycatcherOP
Trying to get this to log the output of my GoFetch() to make sure I'm getting the right thing
🤣 I left GoFetch as my server-side saving function! I've been working out of the wrong branch!
Cordilleran FlycatcherOP
I feel your name is more accurate to me than you lol
@Cordilleran Flycatcher I feel your name is more accurate to me than you lol
American black bear
you don't know me, I deserve this name and I like it
Cordilleran FlycatcherOP
LOL well I'm feeling it right now too
American black bear
😂
Cordilleran FlycatcherOP
So I've cppied over some of the scripts in rsoe to route so I can walk through this
import { NextRequest, NextResponse } from "next/server";
//import /*passData,*/ { Category, /*GET_TEST, */GoFetch } from "./rsoe";

 
async function GET(req: NextRequest)
{
    console.log("Get Route reached!")
    const res = await GoFetch(Category[0])
    
    return NextResponse.json({});
}




    const site = "https://api.rsoe-edis.org/"
    let events = "event/Events";
    let key = process.env.RSOE_TOKEN;
    let Category = Object.create(null, { // Add more values or remove values for more categories.
        Tsunami: {value: ["HY","TSU"], enumerable:true},
        Earthquak:{value: ["GE","ERQ"], enumerable:true},
        Cyclone:{value:["WE","CYC"], enumerable:true},
        Volcano:{value:["GE","VOE"], enumerable:true},
        ExtremeRain:{value:["WE","EXR"], enumerable:true},
        SevereWeather:{value:["WE","SEW"], enumerable:true},
        Tornado:{value:["WE","TOR"], enumerable:true}

});


////////////////////////////////////////////
async function RSOE_Fetch(url="",data={})
{
    console.log("DEBUG FETCH");
    const response = await fetch(url,data);
    
    return response.json();
}

function get_category(cat: string[2])
{
    let url=site+events+"?apiKey="+key;
    url+="&category="+cat[0]+"&subCategory="+cat[1];

    return url;
}

async function GoFetch(c: string[2])
{
    return RSOE_Fetch(get_category(c),{});
}
Cordilleran FlycatcherOP
Oh interesting.
export async function GET(req: NextRequest)
{
    console.log("Get Route reached!")
    const res = await GoFetch(Category[0])
    
    return NextResponse.json({message:"Test"});
}

This throws my 500 error, so the failure isn't coming through mapbox it's coming through my GoFetch.
So I'm moving my url components into route.
import { NextRequest, NextResponse } from "next/server";
import /*passData,*/ { /*Category, /*GET_TEST, */GoFetch } from "./rsoe";

 
export async function GET(req: NextRequest)
{

    const check = await fetch(site);
    
    return NextResponse.json({message:"Test"});
}

const site = "https://api.rsoe-edis.org/"
let events = "event/Events";
let key = process.env.RSOE_TOKEN;
let Category = Object.create(null, { // Add more values or remove values for more categories.
    Tsunami: {value: ["HY","TSU"], enumerable:true},
    Earthquak:{value: ["GE","ERQ"], enumerable:true},
    Cyclone:{value:["WE","CYC"], enumerable:true},
    Volcano:{value:["GE","VOE"], enumerable:true},
    ExtremeRain:{value:["WE","EXR"], enumerable:true},
    SevereWeather:{value:["WE","SEW"], enumerable:true},
    Tornado:{value:["WE","TOR"], enumerable:true}

});
replacing message with check, results in my 200. Good news so far!
Witness in real time a man baffled by this process unfold the secrets.
But that's a very good sign. That's stating that I'm getting an ok status from rsoe
😦 Me dumb.
function get_category(cat: string[2])
{
    let url=site+events+"?apiKey="+key;
    url+="&category="+cat[0]+"&subCategory="+cat[1];

    return url;
}

I MAY have found the problem.....
Cordilleran FlycatcherOP
export async function GET(req: NextRequest)
{
   // const check = await fetch(get_category(Category.Tsunami));
    const check = await GoFetch(Category.Tsunami);// <---
    //Category.X not Category[index]
    return NextResponse.json(check);
}
Answer
Cordilleran FlycatcherOP
So using that I'm getting my OK.
I'm trying to walk through to my data in Grab() 🙂
Cordilleran FlycatcherOP
Thank you all for the help! I'm laughing at how I managed to get that issue.

I appreciate it all!
Cordilleran FlycatcherOP
Aaaaaand bob's your uncle.
rsoe.tsx
import { type } from "os";

const site = "https://api.rsoe-edis.org/"
let events = "event/Events";
let key = process.env.RSOE_TOKEN;
export let Category = Object.create(null, { // Add more values or remove values for more categories.
    Tsunami: {value: ["HY","TSU"], enumerable:true},
    Earthquak:{value: ["GE","ERQ"], enumerable:true},
    Cyclone:{value:["WE","CYC"], enumerable:true},
    Volcano:{value:["GE","VOE"], enumerable:true},
    ExtremeRain:{value:["WE","EXR"], enumerable:true},
    SevereWeather:{value:["WE","SEW"], enumerable:true},
    Tornado:{value:["WE","TOR"], enumerable:true}
});

export function get_category(cat: string[2])
{
    let url=site+events+"?apiKey="+key;
    url+="&category="+cat[0]+"&subCategory="+cat[1];
    return url;
}
export async function GoFetch(c: string[2])
{
    return (await fetch(get_category(c),{})).json();
}
route.tsx
import { NextRequest, NextResponse } from "next/server";
import /*passData,*/ { Category, /*GET_TEST, */GoFetch } from "./rsoe";

 
export async function GET(req: NextRequest)
{
   // const check = await fetch(get_category(Category.Tsunami));
    const check = await GoFetch(Category.Tsunami);
    console.log(check);
    return NextResponse.json(check);
}
mapbox.tsx : Grab()
        async function grab()
        {
           const res = await fetch('api/rsoe',{
            method: 'GET',
            headers: {
                'Content-Type': "applications/json" 
                //'Access-Control-Allow-Origin':'*'               
            },
            //mode:"cors"
           })
           console.log(res.json());
           
        }
        grab();
We're outputting feature collections! WOO 😄
American black bear
Congrats, fixed the problem your self 🔥