I am creating a new site using NextJS, the issue i am having is in regards to a password reset verification endpoint.
When a user triggers a password reset, it goes to the API, does all the processing and then returns them to the NextJS frontend at /verifyreset, which saves a code into localstorage, does a small bit of processing and then forwards them onto another page.
The issue is that there is a Default layout wrapping the component in my _app.js like so;
function MyApp({ Component, pageProps }) {
return (
<DefaultLayout><Component {...pageProps} /></DefaultLayout>
);
}
which means that the layout shows on the /verifyreset endpoint, and I only want that endpoint to process data.
Is there any way around this? to have an endpoint that can access localstorage but not be a 'page' so to speak
I did partially understand your question, it would have been clear if you had attached more code snippets in the question.
Anyway, from your statement below:
When a user triggers a password reset, it goes to the API, does all
the processing and then returns them to the NextJS frontend at
/verifyreset, which saves a code into localstorage, does a small bit
of processing and then forwards them onto another page.
what I understood is:
User triggers a password reset [lets say from PageA]
API is invoked; some processing happen
API then, redirects user to /verifyreset page [lets say it PageB]
Navigating to the page, information is saved into localstorage
Once that is completed, user is redirected to another page [lets say it PageC]
Correct me if I am wrong, so your question is, how could you actually skip users to view /verifyreset page but do the things like save to localstorage and other background operations.
Answer 1: The api is being invoked from PageA (see 1). Instead of the api redirecting user to /verifyreset page on the frontend, send some data (JSON or XML) to the calling function (in PageA components..). Based on that data, do the processing and once every thing is complete, redirect the user to PageC. [no need to worry about PageB i.e. /verifyreset page]. Please find the code snippet below:
**API End Point**
async resetPassword(req, res) {
try {
const model = model.body || {};
let data = await PasswordBusiness.reset(model);
//data needs to have information that you require on frontend
return res.json({success: true, data: data});
} catch (error) {
return res.json({success: false, error: error});
}
}
** Frontend - pageA **
import Router from 'next/router';
const resetPassword = (model) => {
callApiEndPoint(model).then(data) {
// do what you want to do with data
//finally navigate to page c
Router.push('url-to-page-c');
});
};
return <button onClick={resetPassword}> Reset </button>
Answer 2: If you require redirecting to the page any how from the API [I think you don't necessary require this], once operation/processing is completed on API end, redirect the user directly to the pageC with some query params with data (if they are not security vulnerable data). e.g. /pagec?token=sometokens&otherinfos=otherinfos and do things on pageC itself. Once completed, remove the query string from the page without refreshing the page.
You have to put /verifyreset at the api folder.
This is what Next.js said in their documentation :
Any file inside the folder pages/api is mapped to /api/* and will be treated as an API endpoint instead of a page.
Reference : https://nextjs.org/docs/api-routes/introduction
Related
I have an application where I want to redirect the user to a page containing the first result of a array of data once is it fetched.
For example:
The user enters on a menu
He clicks on a button that redirects them to a page /toto
My app fetch the API /toto and redirect the user to /toto/[id]
If there is no content, the user stays on the page /toto and the front tell them that there is no data yet.
What I am doing right now:
const {data, isLoading} = useGetToto();
useEffect(() => {
if(data?.results.length > 0) {
router.push(`/toto/${data?.results[0].id}`
}
}, [data])
if(data?.results.length === 0)
return <p>No content yet</p>
return <p>Loading...</p>
I wonder if there is a better way to do this. Using a useEffect is probably a bad practice.
The best solution would be to add correct links to navigation. So you will need to check if page has only one result and display link to the result instead. But this a lot depends on your backend - can you do that efficiently (without doing a lot of requests or fetching a lot of data).
The second solution is in your example, you redirect using useEffect. The only thing I recommend is using router.replace. Cause in your case you create new browser history entry, so if you click back in the browser, your page with single entry will be rendered and again useEffect is triggered. And if you use router.replace, instead of creating new browser history entry, it will replace listing page with entry page url (so back actions will go 2 pages back).
I have a Next.js app in production. My content is managed via Strapi. I fetch all the content with getStaticProps function on each page (2 pages actually, small blog).
However, when I create new content or update an existing one, my Next.js app doesn't fetch the new or updated content.
Maybe due to the fetching at build time? If yes, can I have the possibility to fetch the content on client side and have the benefit of SEO?
Any idea?
Many thanks! :)
Considered setting up a revalidate policy for Incremental Static Regeneration to freshen-up stale content.
https://nextjs.org/docs/basic-features/data-fetching#incremental-static-regeneration
// This function gets called at build time on server-side.
// It may be called again, on a serverless function, if
// revalidation is enabled and a new request comes in
export async function getStaticProps() {
const res = await fetch('https://.../posts')
const posts = await res.json()
return {
props: {
posts,
},
// Next.js will attempt to re-generate the page:
// - When a request comes in
// - At most once every 10 seconds
revalidate: 10, // In seconds
}
}
If that doesn't fit your update policy, you'll have to move to server-side rendering and fetch data on each request.
I'm struggling to initialize my Vuex store with the account details of the logged in user from localStorage.
I've been through as many examples as I can of Auth using Nuxt, and none of them demonstrate how on the client side to pull an authToken from localStorage to re-use with subsequent API requests when the user refreshes the page or navigates to the App
Disclaimer: I'm not using Nuxt in SSR (this might affect your answer).
What is annoying is that I can actually load from localStorage and initialize my state but then it gets overwritten. I'll show you what I mean with this small code example:
buildInitialState () {
if (!process.server) {
// Query the localStorage and return accessToken, refreshToken and account details
return {accessToken: <valid-string>, refreshToken: <valid-string>, account: <valid-json-blob>}
} else {
// Return "empty map", we use the string "INVALID" for demonstration purposes.
return {accessToken: "INVALID", refreshToken: "INVALID", account: "INVALID"}
}
}
export const state = () => buildInitialState()
If I put a breakpoint on buildInitialState I can see that I correctly initialize the state with the value of localStorage, i.e. I get the accessToken and refreshToken, etc.. back.
All seems well.
But then .....in another part of the program I'm using Axois to send requests, and I use an axios interceptor to decorate the request with the accessToken. To do this I have to stick it into a plugin to get access to the store.
Something like so:
export default ({ app, store }) => {
axios.interceptors.request.use((config) => {
const accessToken = _.get(store.state, ['account', 'accessToken'])
if (accessToken) {
config.headers.common['x-auth-token'] = accessToken
}
return config
}, (error) => Promise.reject(error))
}
Here the store is closed over in the arrow function supplied to axios so when I go to send the request it sees if there is a valid accessToken, and if so then use it.
BUT, and here's the kicker, when a request is made, I look at the store.state.account.accessToken and low and behold its been reinitialized back to the value of "INVALID".
What? Que?
It's almost like the store was reinitialized behind the scenes? Or somehow the state in the plugin is "server side state"?? because if I try and log buildInitialState I don't get any messages indicating that the path that produced a map with INVALID is being run.
Basically, I don't thoroughly understand the initialization pathway Nuxt is taking here at all.
If any Nuxt masters could help me out understand this a bit more that would be great, it's turning into a bit of a show stopper for me.
Essentially! All I want to be able to do is save the user so that when they refresh their page we can keep on running without forcing them to re-login again....I thought that would be simple.
Thanks and regards, Jason.
I've solved this with a bit of experimentation and comments from other posters around what is called SSR and SPA.
Firstly, this https://github.com/nuxt/nuxt.js/issues/1500 thread really helped me and the final comment from #jsonberry steered my mind in the right direction, away from fetch and asyncData.
I finally had a bit more of an understanding of how NUXT.js was separating SSR and SPA calls.
I then tried #robyedlin suggestion of putting localStorage initialization in the created() method for my main layout/default.vue page.
While I made progress with that suggestion it turns out created() is also called SSR and I was still trying to initialize my store from credentials that weren't accessible.
Finally, moving the initialization to mounted() did the trick!
So in summary:
My account store is left alone, I don't try and initialize it when it is created (it's just overwritten at some point when the SSR stuff runs)
On mounted() in layout/defualt.vue I read from localStorage and initialize the account store so I can start making API requests with the appropriate accessToken.
That seems to have done the trick.
I am still a little confused about the way Ember fetches data from remote API and save them in the browser.
So I have created a REST Adapter to get sets of records with an Ajax call, a serializer, and the corresponding model. Suppose they are posts, and I have successfully made a posts index page where the user can click on any post to get into the post detail page.
The Ajax call happens on the index page, and using the Ember inspector, it is clear that all the records are stored in the local store.
But when I click the "back link" which is available on every post detail page, it does redirect to '/posts/' but it seems to make the ajax call again. So all the posts are fetched from the API once again making the page much less responsive.
Here's my questions:
How does that part of Ember work and how do I make Ember simply get the records from the local store without making Ajax call again and again? (unless the user refresh the browser)
If I make a GET request to 'post/1' , no data will be available since in this route no Ajax call should be made. But how do I let the data show? Should I set up another REST adapter for individual post or is it possible to get the record from the local store if an Ajax call has been made?
Hope that makes sense and thanks in advance!
Update:
My post adapter:
App.PostAdapter = DS.RESTAdapter.extend({
findAll: function(store, type, sinceToken) {
var url = 'THE URL TO GET JSON BACK';
return $.getJSON(url).then(function(data) {
return posts;
})
}
});
My Post and Posts routes:
App.PostRoute = Ember.Route.extend({
model: function(params) {
return this.store.find('post', params.postId);
}
})
App.PostsRoute = Ember.Route.extend({
model: function() {
return this.store.find('post');
}
})
Regarding your first question: It depends on the model callback of your route. If you use the all method of the store, the Ajax Request won't be made again. (But: You'd be responsible to get the data the first time around. You way want to sideload it somewhere or may want to call find if all didn't return anything. It depends on your application.
Regarding your second question: The RESTAdapter should be available for single data items as well as for lists. You can implement a model hook in the route using find. If you link-to this route with an object (instead of an ID), this hook won't be called. So the hook would only be called when needed.
I have in the frontend an ember.js application with ember-data to abstract the models and in server side I have a REST Api. When I need a list of the items in the server I do a GET request for /items in the case that the list have more than 10 items I receive a json just like that:
{
nextPageToken:"token",
items:[
...
]
}
The nextPageToken is a token to be used to get the items in the next page, in this particular case making another GET request with the page token included: /items?pageToken=token.
I don't know the best way that I can use this tokens to create a page showing the items with a link to the next and the previous pages (storing the token from the previous page) with ember and ember-data.
UPDATE
After some research I found a solution to navigate to next page extending the extractMeta function in the RESTSerializer to store the nextPageToken as metadata.
App.ApplicationSerializer = DS.RESTSerializer.extend({
//...
// Extract the pageToken and store it as metadata from the desired type.
extractMeta: function(store, type, payload) {
store.metaForType(type, { nextPageToken: payload.nextPageToken });
}
}
And then in the controller from my page create a property based in the stored page token.
App.ItemsController = Ember.ArrayController.extend({
//...
// Get the page token store as metadata from the item model.
nextPageToken: function(){
var meta = this.store.metadataFor("item");
return meta.nextPageToken;
}.property('item')
}
Now it's possible to use the page token in your template to create a link to the next page.
With that, part of the problem is solved, but I still need to find a solution to navigate to previous pages.