fetch weatherapi.com with async await react hooks - react-hooks

Can anyone help me why my code doesn't work to fetch API?
I have to build a weather app from several components, must build it structured.
My plan is to have one service component that I have API service in there. then I have to make 3 more components, search component to handle the city search, weatherToday component to show today weather, and weatherForecast component to show five days forecast.
And I have to fetch the API with async an await. Here is the code that I tried(just now I have the code in my App.js just to try if my fetch work)
import { useState, useEffect } from "react";
function App() {
const [data, setData] = useState();
const fetchData = async () => {
await fetch(
`http://api.weatherapi.com/v1/forecast.json?key=1d172d3904e246849d3183628230802&q=Stockholm&days=6&aqi=no&alerts=no`
)
.then((response) => {
return response.json();
})
.then((data) => {
setData(data);
});
};
useEffect(() => {
fetchData();
}, []);
return (
<>
<h3>{data.location.name}</h3>
<p>{data.current.temp_c}</p>
<p>{data.location.localtime}</p>
</>
);
}
export default App;

First of all, welcome to React and StackOverflow!
There's a few issues here:
The main issue is CORS. You can't call this API from your browser - it's meant to be called from a server (backend). I highly recommend using Next.js since you like React, it uses that as it's framework - but it allows you to have Server Components, essentially an Express backend, so that you can perform this API call - then retrieve that data using this client component just to display the data (not to fetch it).
Another issue (but not the problem here) is reusing the data variable in the local scope of then((data) => is not good when you have data defined higher up in the component scope for your state. Use then((d) => instead.
I created a Next.js 13 sandbox for you with this working API call to get you started:
https://codesandbox.io/p/sandbox/broken-field-4nsp1p
In Next.js 13, you can use the app folder, where every component is a Server Component by default. Then you can create Client Component, like the one you have above - you simply have to add use client to the very top of the file, that's it.
Since Next.js 13 is very new (the app folder and concept of Server Components very new and bound to change), you would want to potentially just stick with the pages folder.
In there, you'll see the client component which calls the api folder's getWeather API call.
Finally, you shared your private key with the public. You need to destroy and regenerate that key now:
From the WeatherAPI.com Docs:
Authentication
API access to the data is protected by an API key. If at anytime, you
find the API key has become vulnerable, please regenerate the key
using Regenerate button next to the API key.
https://www.weatherapi.com/docs/
If don't want to use Next.js - then you'll need to use some sort of backend, like Firebase Functions or Google Cloud Functions, etc. Next.js is probably the easiest thing to adapt if you like React though!
Learn about Next.js 13 & /app folder:
https://beta.nextjs.org/docs/getting-started
Learn about Next.js 12 & /pages folder:
https://nextjs.org/docs
Implementing Fetch via Next.js 13
app/head.tsx
export default function Head() {
return (
<>
<title>Weather App</title>
<meta content="width=device-width, initial-scale=1" name="viewport" />
</>
);
}
app.layout.tsx
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
{/*
<head /> will contain the components returned by the nearest parent
head.tsx. Find out more at https://beta.nextjs.org/docs/api-reference/file-conventions/head
*/}
<head />
<body>{children}</body>
</html>
);
}
app/page.tsx
const App = async () => {
console.log("App.js");
const results = await fetch(
`http://api.weatherapi.com/v1/forecast.json?key=1d172d3904e246849d3183628230802&q=Stockholm&days=6&aqi=no&alerts=no`
);
const json = await results.json();
console.log("json", json);
return (
<>
<h3>{json.location.name}</h3>
<p>{json.location.temp_c}</p>
<p>{json.location.localtime}</p>
</>
);
};
export default App;
When visiting either / or /weather, you will see the same results, since the example is implemented in both v12 and v13:
--- OR --- Implementing Fetch via Next.js 12
pages/weather.tsx
async function getData() {
const res = await fetch("/api/getWeather");
console.log("res", res);
// The return value is *not* serialized
// You can return Date, Map, Set, etc.
// Recommendation: handle errors
if (!res.ok) {
// This will activate the closest `error.js` Error Boundary
throw new Error("Failed to fetch data");
}
const json = await res.json();
console.log({ json });
return json;
}
export default async function Page() {
const data = await getData();
console.log("data", data);
return (
<main>
<h3>{data.location.name}</h3>
<p>{data.current.temp_c}</p>
<p>{data.location.localtime}</p>
</main>
);
}
pages/api/getWeather.ts
import { NextApiRequest, NextApiResponse } from "next";
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
const results = await fetch(
`http://api.weatherapi.com/v1/forecast.json?key=1d172d3904e246849d3183628230802&q=Stockholm&days=6&aqi=no&alerts=no`
);
const json = await results.json();
console.log("json", json);
res.status(200).send(json);
}
Remember to revoke your exposed secret API key.
I hope this helps you start building your app. Good luck!

Related

How do I use next auth getServerSession in next js 13 beta server component in app directory

I'm using next auth v4 with next js 13 beta with server component, everything working fine. But I have a situation where I will need to know the logged user id, since I'm using next auth, I have access to session, I can use useSession() but then I will need to make the component a client component, So I want to use it on server, I can use getServerSession in api since I have access to req & res object, but in next js beta with new app dir, I can't do it. Please let me know if you know how to fix the issue. Thank you
import { getServerSession } from "next-auth";
import { authOptions } from "#/pages/api/auth/[...nextauth]";
const Test = async () => {
const user_id = 1; // How do I get user id from session, user_id is available in session
// I don't have access req & res object in server component.
const data = await getServerSession(request, response, authOptions);
console.log(data);
});
return (
<></>
);
};
export default Test;
Didn't find enough information
I found an answer, in next js 13 beta, you wont need to use request & response object, just use the authOptions, it will work
import { getServerSession } from "next-auth";
import { authOptions } from "#/pages/api/auth/[...nextauth]";
const Test = async () => {
const data = await getServerSession(authOptions);
console.log(data);
});
return (
<></>
);
};
export default Test;

How to cache a Server Side Rendered Page using Dynamic Routes with Next.js

I'm deploying a Next.js Application on AWS using ECS/Fargate (can't use Amplify due to custom logging and monitoring required). My Application is using api-routes and dynamic-routes with Server-Side-Rendering(getServerSideProps).
Lets say for the SSRed page the URL is something like: domain.com/foopage/[id]?powerlevel=10000.
The Code Looks something like:
// pages/foopage/[id].tsx
import React from "react";
import type { GetServerSideProps } from "next";
import Head from "next/head";
export default function FooPage({ id }: Record<string, string>) {
return (
<div>
<Head>
<title>Title</title>
</Head>
<main>
<h1>
Say Hi to {id}!
</h1>
</main>
</div>
);
}
export const getServerSideProps: GetServerSideProps = async (context) => {
context.res.setHeader(
"Cache-Control",
"public, s-maxage=10, stale-while-revalidate=59"
);
const { id } = context.query;
const response = await fetch(
`${process.env.CMS_URL}/api/configurations?id=${id}`
);
const jsonResponse = await response.json();
return {
props: { id },
};
};
I want to cache these pages so that the server doesn't have to generate the page every time.
I know that if you are using Vercel, Edge Caching works without configuration. But I'm bound to use AWS due to business-requirements. So how exactly to do it in AWS? Is it possible to do using Lambda#Edge and if so what could be the possible lambda function?
I know that if you are using Vercel, Edge Caching works without
configuration. But I'm bound to use AWS due to business-requirements.
So how exactly to do it in AWS?
If you want edge caching, you need to use a CDN. Amazon's CDN is CloudFront.

add API key to url

Hi I'm build a wildfire app tracker with react using the nasa API it works in development by using the url directly witch is https://eonet.sci.gsfc.nasa.gov/api/v2.1/events
But when I deploy it. It does not get the data. I obviously need a api key witch I have, but how do I implement it in the url above ?
here is my code..
useEffect(() => {
const fetchEvents = async () => {
setLoading(true)
const res = await fetch('https://eonet.sci.gsfc.nasa.gov/api/v2.1/events')
const {events} = await res.json()
setEventData(events)
setLoading(false)
}
fetchEvents()
// eslint-disable-next-line
}, [])
You could try to create a .env file in which you can set URLS as
REACT_APP_PUBLIC_URL=https://eonet.sci.gsfc.nasa.gov/api/v2.1/events
and then in your app component import as
fetch(process.env.REACT_APP_PUBLIC_URL)

Access data outside of Axios await get?

New to Axios, Vue, NuxtJS.
Most examples I've seen show either .get or await, but not both together. This code was pulled from several using Nuxt JS with headless CMS tutorials and is working, but now I can't seem to figure out how to access the data in other functions outside of the async call.
import axios from 'axios'
export default {
async asyncData ({env, params}) {
const {data} = await axios.get(`${env.cockpit.apiUrl}/collections/get/cat_ruleset?token=${env.cockpit.apiToken}&simple=1`);
return {catrules:data}
}
}
export const addmore = async () => {
const response = await axios.get(`https://jsonplaceholder.typicode.com/users/1`);
return response.data;
}

getInitialProps in never called in NextJS

I have problem with getInitialProps method in NextJS. It is never called. This is project where I have Apollo GraphQL client for some pages and getInitialProps for other. I am not sure how to configure them correctly to work.
Apollo is working fine and fetching data as it should. Problem is that getInitialProps isn't called.
Here is my custom _app.js file
const App = ({ Component, pageProps, apollo }) => {
return (
<ApolloProvider client={apollo}>
<Component {...pageProps} />
</ApolloProvider>
)
}
const API_URL =
process.env.NODE_ENV === "development"
? "http://localhost/wordpress/index.php?graphql"
: "https://page/index.php?graphql"
export default withApollo(({ initialState }) => {
return new ApolloClient({
link: new createHttpLink({
uri: API_URL,
fetch: fetch
}),
cache: new InMemoryCache()
})
})(App, { getDataFromTree })
And here is how I call getInitialProps on page
Coupons.getInitialProps = async function() {
const res = await fetch('http://localhost:8000/data/');
const data = await res.json();
console.log(`Data fetched. Count: ${data.length}`);
return {
shows: data.map(entry => entry.show)
};
};
Also. Pages where I have Apollo fetching data doesn't need to call this REST API. Apollo pages and REST pages are totally different
This problem was fixed by following documentation on https://github.com/zeit/next.js/tree/canary/examples/with-apollo
Thing is that I wrapped whole _app in Apollo provider and right way is to wrap only pages that need Apollo in it.
Other that need getInitialProps should remain as is and call REST API in them.

Resources