I am trying to send a function as one of the value from a remix route loader function
export const loader: LoaderFunction = async ({ request }) => {
const userId = await requireUserId(request)
const user = await getUserById(userId)
const canUserPerformAction = getUserPermissions(user)
const organisations = await Organisation.find({}).exec()
return {
organisations,
canUserPerformAction
}
}
getUserPermissions(user) returns a function with the current user context, which needs to be used in the frontend.
this function always ends up being undefined when I fetch it using useLoaderData() in the default component/ frontend. Is there a better way to do this?
Your loader can either be called during the whole page generation, or using an XHR when navigating in your app.
Your loader have to return a valid Response object, which will usually be a JSON response : https://remix.run/docs/en/v1/api/conventions#returning-response-instances
In your code sample, the returned object is automatically converted to a JSON string, and since functions aren't valid json, it gets stripped.
You will need to call your canUserPerformAction and return its actual result as JSON.
Related
This question already has an answer here:
Next js nested dynamic routes: data fetching via getstaticprops with shared data between routes
(1 answer)
Closed last year.
im fetching a huge json data from an external API inside my getstaticprops. this data will then be divided into parts to be send to other static pages (hundreds of pages) as props.
// page1.tsx
const page1 = ({ page1Data }) => {
...
}
const getStaticProps: GetStaticProps = async () => {
const res = await fetch('https://hugedata')
const jsonData = await res.json()
const { page1Data, page2Data, page300Data, ...allData } = jsonData
return { props: { page1Data } }
}
i only know how to send the staticprops data to the component inside the file like this.
is there a way send these data to other static pages/routes ? (e.g. to page2.tsx)
It's not really possible to send data between pages. But you can see the methods described here.
I suggest you really check router and inspect data inside it, you can find a lot of info about data on the page.
P.S. As I see, you are trying to get a lot of data and you need to use pagination and split it to the parts, you can use this lib
This question already has an answer here:
Fetch error when building Next.js static website in production
(1 answer)
Closed 11 months ago.
I am using Next.js with the Vercel deployment workflow, I am following this guide to try and setup page generation at buildtime. The specific section shows the following example to generate pages based on an external API's response:
// This function gets called at build time
export async function getStaticPaths() {
// Call an external API endpoint to get posts
const res = await fetch('https://.../posts')
const posts = await res.json()
// Get the paths we want to pre-render based on posts
const paths = posts.map(post => ({
params: { id: post.id },
}))
// We'll pre-render only these paths at build time.
// { fallback: false } means other routes should 404.
return { paths, fallback: false }
}
// This also gets called at build time
export async function getStaticProps({ params }) {
// params contains the post `id`.
// If the route is like /posts/1, then params.id is 1
const res = await fetch(`https://.../posts/${params.id}`)
const post = await res.json()
// Pass post data to the page via props
return { props: { post } }
}
I want to do this exactly, however I wrote my API as a Node.js serverless function within the same code repository, it is not an external api.
I tried to do the following to call on my api:
// This function gets called at build time
export async function getStaticPaths() {
const res = await fetch('/api/get-designs');
const designs = await res.json();
// Get the paths we want to pre-render based on posts
const paths = designs.map(design => ({
params: { id: design.id },
}))
return {
// Only posts returned by api are generated at build time
paths: paths,
// Enable statically generating additional pages
fallback: true,
}
}
However I get an error that the fetch api url must be absolute. Because of the way Vercel deploys, I won't always have the same deployment URL, so I don't think I can just use a hardcoded value here. Also, I am suspecting that because this function runs at buildtime, that my function is not running yet, therefore can not be called. I am still trying to wrap my head around this Next.js statically generated site workflow, but basically I am confused because they seem to encourage using serverless functions, and this getStaticPaths method for page generation, but they don't seem to work together unless I am missing something.
Is there a way I can run my api to get these results at build time? Any guidance would be much appreciated!
Thanks!
In this case, we can extract the server logic into a function and that function can be used directly inside your api route file. So, for CR we can use /api/whateverRoute and inside getStaticPaths we can use that function itself directly.
I have a rails application with javascript in in the asset pipeline. The rails db has saved urls and I am trying to make fetch() calls with these urls/endpoints to the JSON api. If I put the url in the browser address bar and remove the quotation marks ("") the endpoint will return the data. I can see in the console the error that the url does not exist. I am working on localhost:3000 and that appears to be appended to every fetch call as you can see here. This will appear in the console with a 404 error.
http://localhost:3000/api.openweathermap.org/data/2.5/weather?q=Seattle,us&APPID=fe2a775f427aa5fc92ce0379937b9ee9
. Any suggestions on how to get these fetch() calls working?
How can I implement this?
So far my application will work until I hit the fetch calls that are made and then return undefined
const BASE_URL = "http://localhost:3000/cities.json"
function getCityData(){
fetch(BASE_URL).then( res => res.json() ).then(function(json){
//cityArray will be an Array of cities
//formatted in a way the api can use
let cityArray = json.map(obj => obj.name);
//cityObjArray will be an array of City objects created
// by the class City with city name and api endpoint attributes
const cityObjArray = cityArray.map( city => new City(city));
//fetchArray pulls out all the api endpoints from the object
const fetchArray = cityObjArray.map( cityObj => cityObj.fetchURL);
debugger
let data = fetchArray.map(function(url){
// this will work until it hits this point
// at this point javaScript will attempt to sanitize the endpoints
//and make fetch(url) with them
//I will get 404 not found and it will show the endpoint as
//starting with localhost:3000 which is were I am running this
let rawUrl = url.replace(/['"]+/g, '');
debugger
fetch(rawUrl).then( res => res.json() )
.then(function(json){
debugger
})
.catch(function(){
console.log("ERROR")
});
});
})
}
class City {
constructor(name){
this.name = name
this.fetchURL = `api.openweathermap.org/data/2.5/weather?q=${name},us&APPID=fe2a775f427aa5fc92ce0379937b9ee9`
}
}
class Adaptor {
constructor(url){
this.url = url
this.data = fetch(url).then( res => res.json())
}
}```
[1]: https://i.stack.imgur.com/vCS0o.png
your URLs need to start with a protocol, http://, https:// or // (relative protocol). Otherwise the fetch() interprets them as relative paths at the current host (localhost:3000 in your example). Try this:
this.fetchURL = https://api.openweathermap.org/data/2.5/weather?q=${name},us&APPID=fe2a775f427aa5fc92ce0379937b9ee9
It is not Rails that was adding localhost:3000 to your paths. Since you omitted the protocol it is assumed by fetch() that you have given it a path relative to the current URL. Here is a more detailed answer about relative paths https://stackoverflow.com/a/36369553/632688.
According to the documentation I've read ref.getDownloadURL(); should return a link I could use in an img src.
Here is my code:
This is how I fire up firebase:
this.storage = app.storage();
storageRef = img => this.storage.ref(img);
I then call it like so:
const householdPics = (data, props) => {
const ref = props.firebase.storageRef(`images/${data.profilePic}`);
const img = ref.getDownloadURL();
console.log(img);
}
data.profilePic is equal to something.jpg.
I can confirm it's in storage in firebase in a directory called
images/
The error I get in my console is:
"Firebase Storage: Object 'images/NULL' does not exist."
From firebase I can copy the path:
gs://urlstuff.com/images
Then all my images are listed.
What am I doing wrong?
To get result of async method, you have to use then to get final url with access code attached to it.
ex.
storageRef
.getDownloadURL().then(url => { console.log(url) });
Here, your url will be printed at console.
Two things are wrong here.
First of all, the error message is suggesting that your value of data.profilePic is null. That's not valid - be sure to validate your data.
Second of all, as you can see from the API documentation, getDownloadURL() doesn't return the URL directly. It's asynchronous and returns a promise that resolves when the URL is available. This means you have to await it or use then to capture its final value, after the async work is done.
I have several pages that reference the same node in firestore, each pulling different segments from the firestore node. For example, a summary page might pull through album title, date, genre and image, whilst another page might pull through just the title, artist and record label. A couple of questions:
Is it possible to turn one of the firestore queries into a service?
If so, does that mean the data is only read once whilst navigating across different pages (angular components) that use the same service?
Will the query only run again when data is modified in firestore through the observable? ("return Observable.create(observer => {" )
I have tried a service with the code below. However, the issue observed is that on page refresh, the data isn't present. It is however present whilst navigating through the site. I believe this is because my page is running before the observable is returned. Is there a way to wrap up the query as an observable?
Any assistance would be greatly appreciated.
getAlbumData() {
this.albumDoc = this.afs.doc(`albums/${this.albumId}`);
this.album = this.albumDoc.snapshotChanges();
this.album.subscribe((value) => {
// The returned Data
const data = value.payload.data();
// Firebase Reference
var storage = firebase.storage();
// If album cover exists
if (data.project_signature != undefined) {
// Get the Image URL
var image = data.album_cover_image;
// Create an image reference to the storage location
var imagePathReference = storage.ref().child(image);
// Get the download URL and set the local variable to the result (url)
imagePathReference.getDownloadURL().then((url) => {
this.album_cover = url;
});
}
});
}
When I build my observables, I try to use operators as much as I can until I get the data I want to display in my UI.
You don't want to implement too much code in the subscribe method because you break the reactive paradigm by doing so.
Instead, extract you data in your observable and display it in your template.
Don't forget to use the async pipe in your template to display your data when it gets fetched by your application.
I would do something like this:
// In AlbumService
getAlbumCover(albumId: string) {
const albumDoc = this.afs.doc(`albums/${albumId}`);
const album_cover$ = this.albumDoc.snapshotChanges().pipe(
// get the album data from firestore request
map(data => {
return {
id: data.payload.id,
...data.payload.data()
};
}),
// emits data only if it contains a defined project_signature
filter(album => album.project_signature),
// prepare the imagePath and get the album cover from the promise
mergeMap(album => {
const storage = firebase.storage();
const image = album.album_cover_image;
const imagePathReference = storage.ref().child(image);
return imagePathReference.getDownloadURL();
})
);
return album_cover$;
}
By doing so, when your data is updated in firestore, it will be fetched automatically by your application since you use an observable.
In your component, in the onInit() method after getting your album id from the url:
this.album_cover$ = this.albumService.getAlbumCover(albumId);
Finally, in my template, I would do :
<div>{{album_cover$ | async}}</div>