How to refresh occasional changes with getStaticProps() in next.js? [duplicate] - next.js

This question already has answers here:
Next.js ISR ( Incremental Static Regeneration ), how to rebuild or update a specific page manually or dynamically before the interval/ISR time start?
(3 answers)
Closed 8 months ago.
Here's a scenario;
I am adding some static pages in Next.js, so using getStaticProps() sounds good.
But, a user(admin) can also update [from an admin console] the content of these page in future, not very frequently.
So, Is there any way to refresh or re-deploy the pages only after the changes happen?
or, if We can add a button on admin console to refresh/re-deploy after changes!?
Update: All the Page Content is getting stored in Database, and Next.js fetch the data from DB during build process.
My Approach:
use revalidate with getStaticProps(). But, it also doesn't gurantee immediate update, and importantly, causes unnecessary revalidation, as the changes are rare.
Use getServerProps()
As already said, the changes are occasional, it doesn't sounds best to me to use getServerProps().
Thanks!

This is now possible, using On Demand Revalidation, as of NextJS v12.1. However, On Demand Revalidation is still in beta as of the writing of this answer. On Demand Revalidation is documented here. You simply create a revalidation api endpoint and then call it passing a known secret.
Here is the sample api endpoint from the documentation:
export default async function handler(req, res) {
// Check for secret to confirm this is a valid request
if (req.query.secret !== process.env.MY_SECRET_TOKEN) {
return res.status(401).json({ message: 'Invalid token' })
}
try {
await res.unstable_revalidate('/path-to-revalidate')
return res.json({ revalidated: true })
} catch (err) {
// If there was an error, Next.js will continue
// to show the last successfully generated page
return res.status(500).send('Error revalidating')
}
}
Then to force revalidation of the content you would simply call this api endpoint like this:
https://<your-site.com>/api/revalidate?secret=<token>

Related

What is the difference between axios and useFetch (nuxt 3)?

I'm slowly starting to migrate from nuxt 2 to nuxt 3.
Previously I used to use axios.
In Nuxt3, it is recommended to use useFetch
However the behavior is quite weird with useFetch. Calls are not made systematically.
For example in this piece of code :
async mounted() {
const store = useAuth();
let response = await axios.get('http://dev.test.fr/api/secured/admin', {headers : store.authHeader() });
this.sensibleInformation = response.data;
},
With Axios, every time I open this page, the call is made and the sensibleInformation is up to date.
With useFetch, the syntax is similar
async mounted() {
const store = useAuth();
let response = await useFetch('http://dev.malt.fr/api/secured/admin' , {method : 'get', headers : store.authHeader() });
this.sensibleInformation = response.data;
},
But the call to the server is done... sometimes. So, the sensibleInformation is most of the time empty. And I don't find any explanation in the documentation.
Maybe there is something I miss here.
I'm using nuxt 3.0.0-rc.6
As it is explained in nuxtJS3 useFetch
useFetch is a wrapper for $fetch(come from here ohmyfetch)
you don't need to import this lib it is include in vuejs3 and lastly axios was not compatible with vuejs3 explained here why use $fetch
what is really great is that body is automatically parsed in JSON, so no need to parse or stringify anything. Also header for content type is automatically added.
So no need to import any library, automatic parsing, automatic header detected etc...
Not sure about this one, but I think the "useFetch" helper is designed to be used with the Vue composition API, so :
within the "setup" function
directly in your script tag if you're using the "<script setup>" synthax
The issue you are dealing with maybe due to the fact that you're using "useFetch" within the "mounted" hook of Vue.js options API.
But once again, not sure about this one :)
The major difference between useFetch and Axios is that useFetch is a wrapper around useAsyncData (and native $fetch) and so works with both SSR and Static modes of Nuxt.
If using it in the onMounted hook you will probably get more expected results if you set the server option to false so it runs only in the client (more like how Axios runs in the mounted hook). I have predominantly used it in <script setup> for SSR set ups.
More info here: https://v3.nuxtjs.org/api/composables/use-fetch

Linking images from Firebase Storage to Firestore document and displaying them in React Native

Background
I'm trying to upload images to firebase storage manually (using the upload file button in the web page), however I have no clue how to later link them to a firestore document. What I have come up with (I'm unsure if it works) is copying the url for the image in the storage bucket and adding it to a string type field in the document called profilePicture. The reason I'm unable to get this to work is that I'm really new to React Native and I don't know how to properly require the images other than typing in the specific local route. Mind you also, the way I'm requiring user data such as a profile name is after logging in with email/password auth I pass the data as a param to react navigation and require it as extraData.
What I have tried
Once I've copied the image url and pasted it in the firestore document I'm doing this:
const profilePicture = props.extraData.profilePicture;
<Image source={require({profilePicture})}/>
I have also tried using backticks but that isn't working either. The error message I'm getting is:
TransformError src\screens\Profile\ProfileScreen.js: src\screens\Profile\ProfileScreen.js:Invalid call at line 27: require({
profilePicture: profilePicture
})
Note: this is an expo managed project.
Question
Is the problem in the code or in the way I'm linking both images? Maybe both? Should I require the document rather than relying on the data passed previously?
Thanks a lot in advance!
Edit 1:
I'm trying to get all info from the current user signed in, after a little research I've come to know about requiring images in this manner:
const ref = firebase.storage().ref('path/to/image.jpg');
const url = await ref.getDownloadURL();
and then I'd require the image as in <Image source={{uri: url}}/>
I get that this could be useful for something static, but I don't get how to update the ref for every single different user.
Edit 2:
Tried using the method mentioned in Edit 1, just to see what would happen, however It doesn't seem to work, the image just does not show up.
Maybe because my component is a function component rather than a class component (?
I understand that your goal is to generate, for each image that is uploaded to Cloud Storage, a Firestore document which contains a download URL.
If this is correct, one way is to use a Cloud Function that is triggered each time a new file is added to Cloud Storage. The following Cloud Function code does exactly that. You may adapt it to your exact requirements.
exports.generateFileURL = functions.storage.object().onFinalize(async object => {
try {
const bucket = admin.storage().bucket(object.bucket);
const file = bucket.file(object.name);
// You can check that the file is an image
const signedURLconfig = { action: 'read', expires: '08-12-2025' }; // Adapt as follows
const signedURLArray = await file.getSignedUrl(signedURLconfig);
const url = signedURLArray[0];
await admin.firestore().collection('profilePictures').add({ fileName: object.name, signedURL: url }) // Adapt the fields list as desired
return null;
} catch (error) {
console.log(error);
return null;
}
});
More info on the getSignedUrl() method of the Admin SDK here.
Also note that you could assign the Firestore document ID yourself, instead of having Firestore generating it as shown in the above code (with the add() method). For example, you can add to the image metadata the uid of the user and, in the Cloud Function,get this value and use this value as the Document ID.
Another possibility is to name the profile image with the user's uid.

$http returning error response NULL on first call after launch (ionic) everytime, but after subsequent http post its ok

Whenever I launch my app, and click on login on the first few tries, the login will attempt a POST http to the server. However $http always (everytime) returns NULL on first try. sometimes after several few tries still NULL if done fast. But subsequently, its all ok.
I dont get it, why is $http returning error response NULL initially ??
Here is my login controller doing the http post
Login Controller (LoginCtrl)
https://gist.github.com/anonymous/771194bc5815e4ccdf38b57d6158853f
var req = {
method: 'POST',
url: baseURL,
data: postObject,
//timeout: 5000
};
err is NULL here:
}).error(function(err) {
I dont know if it is CORS but I'ved got this set in config.xml
<access origin="*" />
my config.xml
https://gist.github.com/anonymous/b2df3a857338d14ec3fcd6dda776e212
Any ideas ?
Im using ionic 1.7.14
on device iOS 9.3.1
UPDATE
I'ved put the problem code here. can logout first to goto login screen. enter in anything in username/password field, click login once failed, second or third try will be success.
https://github.com/axilaris/ionic_null_http_problem
some troubleshooting so far: i noticed the http post request is called twice. not sure why.
UPDATED the code using $http.post.then but still has the same effect
$http.post(baseURL, postObject).then(function successCallback(response)
response has NULL data --> Object {data: null, status: 0, config: Object, statusText: ""}
It is hard to diagnose having the above details only.
However the problem could be that your handler (login function) is triggered before digest cycle finished updating $scope.data.username and $scope.data.password and for the first tries it sends empty values for those to the server and works fine later.
You can run Safari web inspector to see what is sent to the server to prove this.
The fix may depend on how your view/template is coded. Can you please share it? Or, ideally, create a working sample at http://play.ionic.io/
Another option to fix could be to try to wrap your code related to http request into
$timeout(function() {
// your code goes here
});
or, consider using .$applyAsync() (see the docs for details)
This might help to fix the problem
You are probably getting this inconsistent behavior as you are using the 'success' promise method instead of 'then' (note that use of the success method has now been deprecated).
The key differences between these two methods are:
then() - full power of the promise API but slightly more verbose
success() - doesn't return a promise but offeres slightly more convienient syntax
as highlighted in this answer.
Hence in your scenario, instead of using 'success':
var req = {
method: 'POST',
url: baseURL + 'session/login',
data: postObject,
//timeout: 5000
};
$http(req).success(function(resp) {...
use 'then' along with angular's post shortcut method (you don't have to use this shortcut method, but I think it makes the code more succinct) e.g.:
$http.post(baseURL + 'session/login', postObject).then(function successCallback(response) {
// this callback will be called asynchronously
// when the response is available
}, function errorCallback(response) {
// called asynchronously if an error occurs
// or server returns response with an error status.
});
Using 'then' returns a promise resolved with a value returned from a callback, so it should give you a consistently valid result.
it was a timeout in app.js that caused it. was set to 1 second which gives it it arbitrary success rate.
config.timeout = 1000;

How to set request headers asynchronously in typeahead/bloodhound

Environment:
I am using typeahead/bloodhound for a search field in my mobile app (steroids/cordova)
Every request from my app to the API needs to be signed and the signature added to auth headers
Obviously setting the headers in the ajax settings won't work as each request bloodhound sends will be different and require different signatures.
In my first implementation, I was using the beforeSend ajax setting to achieve this. Simply calculate the signature in that function and add it to the request headers.
However, this was not very secure so I have decided to place the secret used and the signature calculation into a Cordova custom plugin's native code to be compiled. Not bullet proof but a reasonable amount of security.
As Cordova plugins are asynchronous, beforeSend became useless in this case. The function will complete before the signing and setting of the headers are done.
So, in summary, the question is: How can I asynchronously calculate and set those headers with typeahead/bloodhound?
ok, the solution seems to be fork and hack. First modify _getFromRemote to remove the need for beforeSend by adding a remote.headers option similar to the remote.replace except that it returns a deferred object
if (this.remote.headers) {
$.when(
this.remote.headers(url, query, this.remote.ajax)
).done(function(headers) {
that.remote.ajax.headers = headers;
deferred.resolve(that.transport.get(url, that.remote.ajax, handleRemoteResponse));
});
} else {
deferred.resolve(this.transport.get(url, this.remote.ajax, handleRemoteResponse));
}
and then modify the get function that uses this to handle the deferred
if (matches.length < this.limit && this.transport) {
cacheHitPromise = this._getFromRemote(query, returnRemoteMatches);
cacheHitPromise.done(function(hit) {
if (!hit) {
(matches.length > 0 || !this.transport) && cb && cb(matches);
}
});
}
now I'm free to use asynchronous native code to sign and set request auth headers :)

Meteor: Can't replace document in restricted collection

I am using Meteor 4.2 (Windows) and I am always getting the "update failed: 403 -- Access denied. Can't replace document in restricted collection" when I am trying to update an object in my collection. Strangely I had no problem inserting new ones, only updates are failing.
I tried to "allow" everything on my collection:
Maps.allow({
insert: function () { return true; },
update: function () { return true; },
remove: function () { return true; },
fetch: function () { return true; }
});
But still, this update fails:
Maps.update({
_id: Session.get('current_map')
}, {
name: $('#newMapName').val()
});
Is there something else I can check? Or maybe my code is wrong? Last time I played with my project was with a previous version of Meteor (< 4.0).
Thanks for your help.
PS: Just for information, when I do this update, the local collection is updated, I can see the changes in the UI. Then very quickly it is reverted along with the error message, as the changes has been rejected by the server-side.
Alright, the syntax was actually incorrect. I don't understand really why as it was working well before, but anyway, here is the code that works fine:
Maps.update({
Session.get('current_map')
}, {
$set: {
name: $('#newMapName').val()
}
});
It seems like it must be related to what you're storing in the 'current_map' session variable. If it's a db object, then it probably looks like {_id:<mongo id here>} which would make the update finder work properly.
I ran into the same issues, and found the following to work
Blocks.update {_id:block_id}, {$set: params}
where params is a hash of all the bits i'd like to update and block_id is the mongo object id of the Block i'm trying to update.
Your note about the client side update (which flashes the update and then reverts) is expected behavior. If you check out their docs under the Data and Security section:
Meteor has a cute trick, though. When a client issues a write to the server, it also updates its local cache immediately, without waiting for the server's response. This means the screen will redraw right away. If the server accepted the update — what ought to happen most of the time in a properly behaving client — then the client got a jump on the change and didn't have to wait for the round trip to update its own screen. If the server rejects the change, Meteor patches up the client's cache with the server's result.

Resources