I'm dealing with a project that uses AWS Cognito. There are some configuration params that needs to be fetched from server with an API call. I keep the API call in a config.js file and use async/await to get response from server like this
const getCognitoConfigs = async () => {
const res = await axios.get(`${apiurl.apiurl}/logininfo`);
console.log(res.data);
return res.data;
};
export default getCognitoConfigs;
And in my index.js (where I set up Cognito), I import the function from config.js file
import getCognitoConfigs from "./config";
const configs = getCognitoConfigs();
Amplify.configure({
Auth: {
mandatorySignIn: true,
region: configs.cognito.region,
userPoolId: configs.cognito.user_pool,
userPoolWebClientId: configs.cognito.app_client_id
}
});
The problem is async await does not stop the program execution so I'm getting 'configs' as undefined. Are there anyways that I can make the app stop until the api call has resolved? Thanks.
If you want to use async/await, you have to wrap index.js in an asynchronous function and add
await getCognitoConfigs();
or you can use promise like
getCognitoConfigs().then(res => Amplify.configure({...}))
Related
I'm new to Next.js and I'm trying to understand the suggested structure and dealing with data between pages or components.
For instance, inside my page home.js, I fetch an internal API called /api/user.js which returns some user data from MongoDB. I am doing this by using fetch() to call the API route from within getServerSideProps(), which passes various props to the page after some calculations.
From my understanding, this is good for SEO, since props get fetched/modified server-side and the page gets them ready to render. But then I read in the Next.js documentation that you should not use fetch() to all an API route in getServerSideProps(). So what am I suppose to do to comply to good practice and good SEO?
The reason I'm not doing the required calculations for home.js in the API route itself is that I need more generic data from this API route, as I will use it in other pages as well.
I also have to consider caching, which client-side is very straightforward using SWR to fetch an internal API, but server-side I'm not yet sure how to achieve it.
home.js:
export default function Page({ prop1, prop2, prop3 }) {
// render etc.
}
export async function getServerSideProps(context) {
const session = await getSession(context)
let data = null
var aArray = [], bArray = [], cArray = []
const { db } = await connectToDatabase()
function shuffle(array) {
var currentIndex = array.length, temporaryValue, randomIndex;
while (0 !== currentIndex) {
randomIndex = Math.floor(Math.random() * currentIndex);
currentIndex -= 1;
temporaryValue = array[currentIndex];
array[currentIndex] = array[randomIndex];
array[randomIndex] = temporaryValue;
}
return array;
}
if (session) {
const hostname = process.env.NEXT_PUBLIC_SITE_URL
const options = { headers: { cookie: context.req.headers.cookie } }
const res = await fetch(`${hostname}/api/user`, options)
const json = await res.json()
if (json.data) { data = json.data }
// do some math with data ...
// connect to MongoDB and do some comparisons, etc.
But then I read in the Next.js documentation that you should not use fetch() to all an API route in getServerSideProps().
You want to use the logic that's in your API route directly in getServerSideProps, rather than calling your internal API. That's because getServerSideProps runs on the server just like the API routes (making a request from the server to the server itself would be pointless). You can read from the filesystem or access a database directly from getServerSideProps. Note that this only applies to calls to internal API routes - it's perfectly fine to call external APIs from getServerSideProps.
From Next.js getServerSideProps documentation:
It can be tempting to reach for an API Route when you want to fetch
data from the server, then call that API route from
getServerSideProps. This is an unnecessary and inefficient approach,
as it will cause an extra request to be made due to both
getServerSideProps and API Routes running on the server.
(...) Instead, directly import the logic used inside your API Route
into getServerSideProps. This could mean calling a CMS, database, or
other API directly from inside getServerSideProps.
(Note that the same applies when using getStaticProps/getStaticPaths methods)
Here's a small refactor example that allows you to have logic from an API route reused in getServerSideProps.
Let's assume you have this simple API route.
// pages/api/user
export default async function handler(req, res) {
// Using a fetch here but could be any async operation to an external source
const response = await fetch(/* external API endpoint */)
const jsonData = await response.json()
res.status(200).json(jsonData)
}
You can extract the fetching logic to a separate function (can still keep it in api/user if you want), which is still usable in the API route.
// pages/api/user
export async function getData() {
const response = await fetch(/* external API endpoint */)
const jsonData = await response.json()
return jsonData
}
export default async function handler(req, res) {
const jsonData = await getData()
res.status(200).json(jsonData)
}
But also allows you to re-use the getData function in getServerSideProps.
// pages/home
import { getData } from './api/user'
//...
export async function getServerSideProps(context) {
const jsonData = await getData()
//...
}
You want to use the logic that's in your API route directly in
getServerSideProps, rather than calling your internal API. That's
because getServerSideProps runs on the server just like the API routes
(making a request from the server to the server itself would be
pointless). You can read from the filesystem or access a database
directly from getServerSideProps
As I admit, what you say is correct but problem still exist. Assume you have your backend written and your api's are secured so fetching out logic from a secured and written backend seems to be annoying and wasting time and energy. Another disadvantage is that by fetching out logic from backend you must rewrite your own code to handle errors and authenticate user's and validate user request's that exist in your written backend. I wonder if it's possible to call api's within nextjs without fetching out logic from middlewars? The answer is positive here is my solution:
npm i node-mocks-http
import httpMocks from "node-mocks-http";
import newsController from "./api/news/newsController";
import logger from "../middlewares/logger";
import dbConnectMid from "../middlewares/dbconnect";
import NewsCard from "../components/newsCard";
export default function Home({ news }) {
return (
<section>
<h2>Latest News</h2>
<NewsCard news={news} />
</section>
);
}
export async function getServerSideProps() {
let req = httpMocks.createRequest();
let res = httpMocks.createResponse();
async function callMids(req, res, index, ...mids) {
index = index || 0;
if (index <= mids.length - 1)
await mids[index](req, res, () => callMids(req, res, ++index, ...mids));
}
await callMids(
req,
res,
null,
dbConnectMid,
logger,
newsController.sendAllNews
);
return {
props: { news: res._getJSONData() },
};
}
important NOTE: don't forget to use await next() instead of next() if you use my code in all of your middlewares or else you get an error.
Another solution: next connect has run method that do something like mycode but personally I had some problems with it; here is its link:
next connet run method to call next api's in serverSideProps
Just try to use useSWR, example below
import useSWR from 'swr'
import React from 'react';
//important to return only result, not Promise
const fetcher = (url) => fetch(url).then((res) => res.json());
const Categories = () => {
//getting data and error
const { data, error } = useSWR('/api/category/getCategories', fetcher)
if (error) return <div>Failed to load</div>
if (!data) return <div>Loading...</div>
if (data){
// {data} is completed, it's ok!
//your code here to make something with {data}
return (
<div>
//something here, example {data.name}
</div>
)
}
}
export default Categories
Please notice, fetch only supports absolute URLs, it's why I don't like to use it.
P.S. According to the docs, you can even use useSWR with SSR.
I'm trying to use the async/await functionality to build a node JS script. I currently have a file called repo.js as a helper file to get data from Github's API and return it to a variable for me to access elsewhere in different JS files of my node application, repo.js is as such:
const axios = require('axios')
const repo = async () => {
const data = await axios.get('https://api.github.com/repos/OWNER/REPO/releases', {
headers: {
'Authorization': 'token MYTOKEN'
}
})
return data
}
exports.repo = repo
And then in my main.js file I'm trying to do...
const repo = require('./src/utils/repo')
program
.option('-d, --debug', 'output extra debugging')
.option('-s, --small', 'small pizza size')
.option('-p, --pizza-type <type>', 'flavour of pizza')
const repoData = repo.repo
console.log(repoData)
Unfortunately, this just returns [AsyncFunction: repo] to the console which isn't the intended behaviour. Why can't I access the contents here?
UPDATE
Based on some responses I've been given, I'm aware of the fact I need my code inside of a async function or to use .then(). The issue is, I don't want to put all of my application's code inside of a .then() just to rely on one thing from an API.
Example:
var version = ''
repo.getRepoDetails().then((res) => {
version = res.data[0].body.tag_name
})
Now I have access to version everywhere.
Every async/await function is a promise, meaning that you need to wait for it to finish in order to read it's result.
repo.repo().then(res => console.log(res))
If you application is a simple nodejs script(or single file) then you can wrap your code inside an IIFE like this:
const repo = require('./src/utils/repo')
(async () => {
program
.option('-d, --debug', 'output extra debugging')
.option('-s, --small', 'small pizza size')
.option('-p, --pizza-type <type>', 'flavour of pizza')
const repoData = await repo.repo() <--- You can use await now instead of then()
console.log(repoData)
})()
Async function always return promise object so you can access the result using promise.then() like
repo.repo().then(result => result)
I'm using nuxt and am looking for a way to access the axios object from within the nuxt fetch() method. Unfortunately I can't seem to access it. This is what I've tried...
async fetch({store, params}) {
// const result = await axios.$get('/api/v2/inventory/3906?apiKey=xxx');
// const result = await this.axios.$get('/api/v2/inventory/3906?apiKey=xxx');
// const result = await $axios.$get('/api/v2/inventory/3906?apiKey=xxx');
store.commit('property/setProperty', result);
}
this is (obviously) not available and $axios and axios are undefined.
I do have #nuxtjs/axios defined in my nuxt.config.js
modules: [
'nuxt-buefy',
'#nuxtjs/style-resources',
'#nuxtjs/device',
'#nuxtjs/axios',
'#nuxtjs/proxy'
],
#balexandre should actually get the credit for this. Thanks for the help.
This is an incredibly stupid question I asked, but I'm going to post the answer here in case anyone else has the same experience brain-fart.
import axios from 'axios' // don't forget me!
... later...
fetch() {
async fetch({store, params}) {
const result = await axios.get('/api/v2/inventory/3906?apiKey=xxx');
store.commit('property/setProperty', result);
}
}
In React we fetch data using useEffect when reloading the page:
useEffect(() => {
let ignore = false
const fetchingData = async () => {
const res = await fetch(<your_path>)
const data = await res.json()
if (!ignore) setData(data)
};
fetchingData()
return () => { ignore = true }
}, [])
But how can I do this in Next.js? Fetch doesn't fire in getInitialProps when the page reloads.
use axios or isomorphic-unfetch. they work both in client and server environments. Fetch API does not exist in the Node.js environment. it. If you still want to use Fetch API in your client-side code, you should use a isomorphic-unfetch. When you are in the browser, it uses the unfetch polyfill. If your code runs in Node.js, it switches to node-fetch.
import "axios" from "axios"
static async getInitialProps(){
const res=await axios.get('url')//
const data=res.data
return {data}
}
UPDATE
In addition to fetch() on the client-side, Next.js polyfills fetch() in the Node.js environment. You can use fetch() in your server code (such as getStaticProps/getServerSideProps) without using polyfills such as isomorphic-unfetch or node-fetch.
https://nextjs.org/docs/basic-features/supported-browsers-features
In Next.js you usually load data with HTTP requests in getInitialProps then you can use them as props:
const App = props => (
<Layout>
{props.data}
...
</Layout>
);
App.getInitialProps = async function() {
const res = await fetch(<your_path>)
const data = await res.json()
return {
data
}
};
export default App;
I have a NextJS app and am using next-routes to handle all routing.
My routing module currently looks like this:
const routes = require('next-routes')();
const { getEntries } = require('../data/contentful');
module.exports = async () => {
const globalSettings = await getEntries({
content_type: 'globalSettings',
});
routes
.add('caseStudies', `/${globalSettings.fields.caseStudiesSlug}`, 'caseStudies')
.add('caseStudy', `/${globalSettings.fields.caseStudiesSlug]}/:slug`, 'caseStudy')
.add('home', `/`, 'index')
.add('page', `/:slug*`, 'page'));
return routes;
};
I can get this working for server side, but to use next-routes on client side, I need this module to immediately return the routes object rather than an async function. e.g.
const routes = require('next-routes')();
const { getEntries } = require('../data/contentful');
// Do this first, then module.exports
const globalSettings = await getEntries({
content_type: 'globalSettings',
});
module.exports = routes
.add('caseStudies', `/${globalSettings.fields.caseStudiesSlug}`, 'caseStudies')
.add('caseStudy', `/${globalSettings.fields.caseStudiesSlug]}/:slug`, 'caseStudy')
.add('home', `/`, 'index')
.add('page', `/:slug*`, 'page'));
This doesn't work because await must be inside an async function. How can I complete my async API call before doing my module.exports of the routes object?
This is a special case of this renowned problem. Synchronous code can be transformed to asynchronous but not vice versa.
As shown in this similar answer, promises should be used up to application entry point if needed:
module.exports = (async () => {
const globalSettings = await getEntries({
content_type: 'globalSettings',
});
return routes
.add('caseStudies', `/${globalSettings.fields.caseStudiesSlug}`, 'caseStudies')
.add('caseStudy', `/${globalSettings.fields.caseStudiesSlug]}/:slug`, 'caseStudy')
.add('home', `/`, 'index')
.add('page', `/:slug*`, 'page'));
})();
This module also exports a promise so it needs to be chained in a module that depends on it.
There is a proposal for top-level await that is intended to provide syntactic sugar for this recipe.