Make requests in getStaticProps - node.js

I am trying to do a simple fetch of pages from my Wordpress installation in my getStaticProps method:
export async function getStaticProps(context) {
// get pages!
const response = await fetch("http://localhost:8000/wp-json/wp/v2/pages");
return {
props: {
}, // will be passed to the page component as props
}
}
When calling that I get:
FetchError: request to http://localhost:8000/wp-json/wp/v2/pages failed, reason: connect ECONNREFUSED 127.0.0.1:8000
So reading the docs a little bit, I know you aren't supposed to make API calls in getStaticProps. However in my case, this is the Wordpress API, not the NextJS API, which I think is what that documentation addresses.
I am able to access my API otherwise just fine. Its like in whatever context calls 'getStaticProps', local host does not exist?
Looking at the wordpress/nextjs example where some API requests are called right within the getStaticProps, the only difference I can see is usage of a GraphQL plugin. I'm assuming that is handling some logic I am overlooking, but I am unsure.
https://github.com/vercel/next.js/tree/canary/examples/cms-wordpress
Thank you for any help in advance!
Joe

Related

How can i write a function to call(POST) an API, from within the web server and not the browser. -NextJS

How can i write a function to call an API, from web server to API within the server itself.
I do not want the browser to know that he is calling an API or and information about the API, even the link.
The API call(POST method) should be executed on the server.
Using Nextjs with axios.
I also want the function to be a component.
If you want to fetch data before loading the page (when the actural page load request happens), use server side rendering.
In Next.js you can use getServerSideProps for doing this. By doing this, you get to keep the api call inside the component, and the sever will fetch the data before rendering your component. Attaching a sample of code that you can refer for this.
export async function getServerSideProps(context) {
return {
props: {}, // will be passed to the page component as props
}
}
For more info, please visit the nextjs official documents. Attaching a link for the above example.
https://nextjs.org/docs/basic-features/data-fetching/get-server-side-props

Page not changing onClick using useNavigate in React?

I have a very basic UI for a login page:
Upon clicking the LOGIN button, the following methods gets called:
async function loginPatient(){
let item ={username:userName, password};
let result = await fetch("http://localhost:8000/users/login",{
method:'POST',
headers:{
"Content-Type":"application/json",
"Accept":"application/json"
},
body: JSON.stringify(item)
});
alert(result);
alert("breakpoint")
result = await result.json();
localStorage.setItem("user-info",JSON.stringify(result));
nav('/patient')
}
At this point I simply want it to change the page when the button is clicked. My API returns the following information from the database:
To test I did console.log("hello world") in the first line of the function and it works
However, If I run console.log("hello world") after the let result = await fetch(...) part it does not work. How can I test this to see why it's not working ?
Here are the errors from the console:
I did not write the API and do not know how Node works yet, I am just doing the front end for this
The issue is code is never reaching after fetch line, basically request is failing, the error on console is saying the due to CORS issue, the request failed, and in your loginPatient function, you have not handled the failed case, if you just wrap your fetch call inside try/catch block, you will see your code will fall into fail block, as api failed.
You need to enable CORS on your server or backend, Cross-Origin Resource Sharing (CORS) is an HTTP-header based mechanism that allows a server to indicate any origins (domain, scheme, or port) other than its own from which a browser should permit loading resources.
You can read more about cors at:
https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
Looks like your client is on some other domain or port(if your are developing locally) than your server. You need to enable CORS permission for your client url.
And if you are using express for your backend, you can check the following url to enable cors.
https://expressjs.com/en/resources/middleware/cors.html
And last thing why Postman is getting success response, because it is by passing this cors check, as Postman is making request from it's server to your direct server instead of browser.
First initialize you navigation variable as follows
const navigate =useNavigate()
then navigate to you specific route by returning you navigation variable as follows.
return navigation("/");
Happy Coding!

How do I properly proxy a request to the Google Maps Embed API?

I'm attempting to use the Google Maps Embed API to embed a map matching an address that a user enters. Following the developer guide, I acquired an API Key to add to my API requests. I'm able to successfully pull up a map when I request it via the "src" attribute of an iframe in my React client, like so:
<iframe
...
src={`https://www.google.com/maps/embed/v1/${mode}?key=${apiKey}&q=${encodedAddressQuery}`}
>
But this leaves my API key exposed to the client.
In Google's API Key Best Practices, it's recommended that the API key be stored in an environment variable and that a proxy server be used to safeguard keys. However when I try proxying the request (code below), it seems to return the appropriate map for a split second, but then the iframe replaces the map with an "Oops! Something went wrong" message, and the browser console displays this error:
Google Maps JavaScript API error: UnauthorizedURLForClientIdMapError
https://developers.google.com/maps/documentation/javascript/error-messages#unauthorized-url-for-client-id-map-error
Your site URL to be authorized: http://127.0.0.1:3001/api/maps?parameters=[my_encoded_parameters]
I'm just developing locally at the moment, so I've tried registering http://127.0.0.1:3001/* as an authorized URL as the error documentation suggests, and I've also tried removing all website restrictions on the API key just to see if I was authorizing the wrong URL, but both of those attempts still produced the same error.
I haven't found many resources on setting up a proxy other than this Google Codelabs project, but I haven't been able to pick anything out of it to help with this issue.
Here's the code I'm using in my React front end:
<iframe
...
src={`http://127.0.0.1:3001/api/maps?parameters=${encodedAddressQuery}`}
>
And in my Express Node.js back end:
router.get('/api/maps', async (req: Request, res: Response) => {
try {
const parameters = req.query.parameters;
const map = await MapDataAccessObject.getOne(parameters);
return res.status(OK).send(map.data);
} catch (err) {
...
}
});
export class MapDataAccessObject {
public static async getOne(parameters: string) {
const apiUrl = this.getMapsEmbedUrl(parameters);
const map = await axios.get(apiUrl);
return map;
}
private static getMapsEmbedUrl(parameters: string) {
const encodedParams = encodeURI(parameters);
return `https://www.google.com/maps/embed/v1/place?key=${process.env.MAPS_API_KEY}&q=${encodedParams}`;
};
}
I'm running my React front end and my Node.js back end in Docker containers.
My best guess is that I'm missing a request header that the Maps API is expecting, but I haven't found any documentation on that, or maybe I'm misunderstanding something more fundamental. Any guidance would be very much appreciated.
There is no way to protect your google maps key on a website from being "spied on" because it is always public. The only way to protect your key from foreign usage is to restrict it's access to only the allowed domain/ip-addresses - so if it is used from someone else, it will not work or take anything from your credits (google will show an error message).
https://developers.google.com/maps/documentation/javascript/get-api-key

How to implement Google API with React, Redux and Webpack

I'm trying to get google calendar events into my React Redux app.
I've tried using googleapis and google-auth-library but webpack is throwing errors because googleapis was built to run server side and bundle.js is referenced from client.
So I've read a few forums about these errors and they all point to using Google's js client library instead.
I understand how to implement this in a java or php app (I'm old... 35 ;) but I'm new to React Redux and I'm looking for the best way to implement this.
I'm trying to fetch the events from my calendar in my actions.js. I tried including <script src="https://apis.google.com/js/api.js"></script> in my html header and then using gapi.load() from actions.js. I also tried creating a api.js file and referencing that with require('./api'). I also tried to use the cli commands from the Node.js Quickstart guide to get an access_token and then just use axios to call Google API directly but I'm getting a 403. I'm thinking I'm just not providing the proper headers but that wouldn't be best practice anyway.
My question is basically how do I reference Google's js client library from my actions.js file while adhering to Redux standards?
You're on the right track by including the official Google client API in the HTML header. It's less than ideal -- it would be nice if Google provided the (browser) client API as an npm module that you could import. But they don't (that I see), so I think what you're doing is fine.
Then there's the question of "how do I use it in a way that's React/Redux friendly?" Redux is a mechanism for managing the state of your application. The Google API is not part of your application (though what you do with it may inform the state of your application).
It's easy to verify that you have access to the Google API: you can just make a call from the componentDidMount method of one of your components, and do a console log:
class MyComp extends React.Component {
componentDidMount() {
// this is taken directly from Google documentation:
// https://developers.google.com/api-client-library/javascript/start/start-js
function start() {
// 2. Initialize the JavaScript client library.
gapi.client.init({
'apiKey': 'YOUR_API_KEY',
// clientId and scope are optional if auth is not required.
'clientId': 'YOUR_WEB_CLIENT_ID.apps.googleusercontent.com',
'scope': 'profile',
}).then(function() {
// 3. Initialize and make the API request.
return gapi.client.request({
'path': 'https://people.googleapis.com/v1/people/me',
})
}).then(function(response) {
console.log(response.result);
}, function(reason) {
console.log('Error: ' + reason.result.error.message);
});
};
// 1. Load the JavaScript client library.
gapi.load('client', start);
},
}
If you don't see what you expect on the console, somehow gapi isn't getting loaded as you expect. If that happens, you'll have a more specific question you can ask!
If you do get a response, you now know how to call GAPI...but then how to make use of it in a Redux-friendly way?
When you make a GAPI call, you probably want to modify your application's state in some way (otherwise why would you be doing it?) For example, you might invoke the auth flow, and when GAPI returns success, your application state now has loggedIn: true or similar (possibly with lots of other state changes). Where you make the GAPI call is up to you. If you want to do it when the component loads, you should do it in componentDidMount. You also may commonly be making the GAPI call in response to a user action, such as clicking on a button.
So the typical flow would be something like this:
// either in componentDidMount, or a control handler, usually:
someGapiCall()
.then(result => {
this.props.onGapiThing(result.whatever)
})
Where this.props.onGapiThing is a function that dispatches an appropriate action, which modifies your application state.
I hope this overview helps...feel free to follow up with more specific questions.
Can you try this library which I used to load external libraries and modules in my React app when I couldn't find a NPM module for it:
https://github.com/ded/script.js/
So your code will be like this:
import $script from 'scriptjs';
$script('https://apis.google.com/js/api.js', function () {
//Put your google api functions here as callback
});
I'm going to answer my own question despite some very good correct answers.
#MattYao answered my actual question of how to get a js script available for reference in my actions.js file.
#Ethan Brown gave a very detailed answer that showed some excellent flow possibilities.
#realseanp changed the scope but a valid answer.
I tried all of the above and they worked.
So I'm not sure what I was doing wrong but I was finally able to access the gapi object from actions.js by just adding <script src="https://apis.google.com/js/api.js"></script> to my index head.
I'm using pug so it looks like this:
doctype
html
head
title MyTitle
link(rel='stylesheet' href='/static/css/main.css')
link(rel='stylesheet' href='/static/css/react-big-calendar.css')
script(src='https://apis.google.com/js/api.js' type='text/javascript')
body
div(id='app')
script(src='/static/bundle.js' type='text/javascript')
Here is my component file:
import React from 'react'
import BigCalendar from 'react-big-calendar';
import moment from 'moment';
import { connect } from 'react-redux'
import { fetchEvents } from '../actions/actions'
BigCalendar.momentLocalizer(moment);
#connect((store) => {
return {
events: store.events.events
}
})
export default class Calendar extends React.Component
{
componentWillMount()
{
this.props.dispatch(fetchEvents())
}
render()
{
return (
<div>
<BigCalendar
events={this.props.events}
startAccessor='startDate'
endAccessor='endDate'
style={{height: 800}}
/>
</div>
)
}
}
And then I was able to get this working in my actions.js file
export function fetchEvents()
{
return (dispatch) =>
{
function start()
{
// 2. Initialize the JavaScript client library.
gapi.client.init({
'apiKey': API_KEY,
// clientId and scope are optional if auth is not required.
'clientId': CLIENT_ID,
'scope': 'profile',
}).then(function() {
// 3. Initialize and make the API request.
return gapi.client.request({
'path': 'https://www.googleapis.com/calendar/v3/calendars/MY_EMAIL#gmail.com/events?timeMax=2017-06-03T23:00:00Z&timeMin=2017-04-30T00:00:00Z',
})
}).then( (response) => {
let events = response.result.items
dispatch({
type: 'FETCH_EVENTS_FULFILLED',
payload: events
})
}, function(reason) {
console.log(reason);
});
};
// 1. Load the JavaScript client library.
gapi.load('client', start)
}}
I had to make my calendar public to access it this way. So now I'm going to work on the oauth2 stuff :/
I would load all the google stuff in my index file before i loaded my webpack bundle (Option 1) . Then I would use redux sagas to call the google apis. Loading the google code before your webpack bundle will ensure everything is ready to go when you call the api from the saga
Try this package.
It looks like it is updated.
https://github.com/QuodAI/tutorial-react-google-api-login

React Node API Request Design Pattern

I need to make an API request to an external API using an API Key. I know how to make this API request in React by writing a onSubmit function. But since I have an API key that I want to keep a secret I am going to write a simple Node app to house env variables.
Besides messing around in node this is my first production experience with Node and I am wondering if my thought process is correct and if not, the better way to do this.
Most of this question will be pseudo code since I haven't started with the Node portion yet.
The idea is that from within the React component it would call the Node app who in turn would call the external API.
React -> Node -> External API
So the React component would be something like so:
handleSubmit: function() {
var data = this.refs.testData.getDomNode().value;
$.ajax({
url: '/my-node-endpoint',
dataType: 'json',
type: 'POST',
data: { test: data },
success: function(data) {
// Whatever success call I want to make
}.bind(this)
})
}
And then in my Node app it would like something like this:
app.post('/my-node-endpoint', function(req, res) {
// Store the values we are posting as JSON
// Start the post request
// On End tell the React component everything is ok
// Prosper
});
As always, thanks for any help that is offered.
Your thought process looks right to me.
If the API you are calling is from a different domain, you will have to build a wrapper on your node server like you did here. Unless the external API supports cross-origin requests with no domain restrictions (such as MapBox web services), you will have to do this.
Several improvements to your code:
As far as I know, you can use React.findDOMNode(this.refs.testData).value instead of this.refs.testData.getDomNode().value. getDomNode() is deprecated in v0.13.
For all the AJAX calls, you can use the Store concept in Flux. The store keeps the states of the data, including updating data through AJAX request. In your React UI code, you just need to call the methods of the store, which makes your UI code clean. I usually create a store class myself without using Flux.

Resources