Best way to create dynamic navigation on NextJS with firebase? - javascript

I'm looking forward to create a web app with NextJS and Google's Firebase. The app has an admin panel and a public site. In the admin panel should have options to edit the public site's navigation.
So I was thinking is it wise to have client-side fetching navigation data on each refresh or is there a better way to get dynamic and up-to date navigation data with as little API request as possible?

This really depends on your use case. In Next.js there are 3 diffrent types for data fetching.
Static site generation - SSG
This fetches the data and renders the pages on build time. This is greate for Pages that don't change to often. Like a blog for example.
Server site rendering - SSR
This fetches the data and renders the pages on each request from a user. This is usefull for data that changes quickly and has to be up to date.
Increment static regeneration - ISR
This allows for a compromise between the 2 types i described above and might be what your looking for if you want to use as little API requests as possiblie. With it you can staticly regenerate the page at a certain time interval.
export async function getStaticProps(){
//fetch data from firebase ...
return {
props: { data },
revalidate: 60,
}
}

Related

How to modularize Javascript authentication code in MFE context in a VueJS

I'm working on a large,single-page VueJS application that's been broken down into several micro front-ends, and I'm currently implementing the authentication for communication with the orchestration layer and it's stumping me. The application is organized as a container application that bootstraps and mounts several other VueJS applications underneath it using single-spa
We use Azure-AD and the MSAL library for authentication and to refresh the Bearer token before each API request, and currently the application I'm working had previously stored the AD client info itself and created the authenticationContext on initialization, something like
export class Authentiation {
authenticationContext;
refreshToken;
constructor() {
this.authenticationContext = new msal.PublicClientApplication(msalConfig)
// code to initialize the
}
async acquireToken() {
// code which refreshes the token
}
}
Now, the initial authentication code has been moved into the container application since it will authenticate the user first and only then mount the apps once the user is inside. Each application, though, needs to be able to refresh the Bearer token whenever it communicates with its OL layer. Currently, it's just running the initialization again and pulling the AuthenticationContext out of local storage using the supplied keys
The goal is to modify it so that a the authentication context from the container is passed down to whatever application is being mounted up, without the need to store the AD client and context information in the app itself
What's required is that every repository within the application would have an API object injected into it, and would use this API when making requests, and the API would contain the authentication context for being able to refresh the Bearer token on each call, and this is where I'm stumped
I had thought about creating an Authentication Singleton that the API could use when being injected into a repository, but since the authenticationContext is unknown at the time, it feels weird to set it after the fact, ie something like
let authContext = new Authentication()
// setup single-spa bootstrap code
// this function is called when the container application bootstraps this Vue application
export const bootstrap = (props) =>
vueLifecycles.bootstrap(props).then(async () => {
authContext.setContext(props.authentication)
})
Then when the API gets intialized, it would have an authContext that would invoke authContext.acquireToken() to get a Bearer token to set for each refresh. But, something feels incorrect about doing this way and I'm not 100% if it's the way to go.
To summarize, applications that were once handling authentication on their own are now being passed the authentication context as a prop during load, and I'm confused about how to implement this to properly set this context as part of the API
Another team had actually stored the acquireToken function in the a Vuex store but that, to me, does not seem appropriate since it ties this code specifically to Vue

View source not displaying dynamic content

I am making very simple next js application, where everything is working fine but except the view source.
I am making a promise and there is a delay in retrieving content and after when those content loaded and if I view source (ctrl + u) in chrome, I couldn't get those dynamic content loaded in the source.
So it is reproduceable in the link,
Step 1) Just click on the codesandbox link: https://3re10.sse.codesandbox.io
Step 2) After that choose view source (ctrl + u), and it gives page like,
Here you could clearly see that there is no element with text My name is Jared and all other text which is intended to be there but it is not.
Only Loading... text is available in page source which comes on page load.
The entire application working code is available here: https://codesandbox.io/s/nextjs-typescript-template-u8evx
Please help me how could I reflect all the dynamic content in view source in Next Js application.
I could understand that this is due to behaviour of async .. But really I couldn't understand the way to overcome this and display the dynamic content once loaded.. Please help me, I am stuck with this for very long..
A big thanks in advance..
You're explicitly telling React to fetch a user on a client-side.
function Profile() {
const { data, revalidate } = useSWR("/api/user", fetch);
}
If you need to prerender a user info on the server you can do it with one of the following functions:
getStaticProps (Static Generation): Fetch data at build time
getServerSideProps (Server-side Rendering): Fetch data on each request.
As you fetching a user info, I assume that it should be requested on each request, so use getServerSideProps.
const URL = 'api/user/'
export default function Profile({ initialData }) {
const { data } = useSWR(URL, fetcher, { initialData })
return (
// render
)
}
export async function getServerSideProps() {
const data = await fetcher(URL)
return { props: { initialData: data } }
}
This way you would fetch a user info on the server and give it to React with first render. Also, you would have useSWR on a client side that will periodically revalidate data.
Suggested reading:
Data fetching
If you use nextjs you must run
yarn build
and
yarn export
then you have directory 'out' with your exported static content.
Because now your example is CSR (client side rendering)
Other answers already quite clear telling you the reason why your dynamic content doesn't appear on source code.
First, there are two kinds of rendering: server side and client side.
Server side / SSR is when your server render your app and then send it to the client (browser).
Client side / CSR is when your app reach the browser, it will be rendered again (but this time only render what is necessary if you have activated SSR, which NextJS has as default).
If you want your dynamic content to be appear at the source code, you should call your api on the server side like #Nikolai Kiselev has mentioned.
NextJS provides function getServerSideProps()(for the component level) to be used if developers want to fetch info on the server side.
If you put your profile() function as a page, you could also use getInitialProps() function to fetch your api from server side.
Please take a look on NextJS doc, they have given the examples you need.

How to auto-generate pages from an API with JavaScript on a WordPress site?

I'm using an API in JSON format to display the standings of sports teams in a league. Here's an example of the JSON data I'm working with:
{ "data":[
{
"position":1,
"team_id":53,
"team_name":"Celtic",
"group_id":null,
"group_name":null,
"overall":{
"games_played":25,
"won":18,
"draw":6,
"lost":1,
"goals_scored":54,
"goals_against":17
},
"home":{
"games_played":13,
"won":9,
"draw":4,
"lost":0,
"goals_scored":30,
"goals_against":8
},
"away":{
"games_played":12,
"won":9,
"draw":2,
"lost":1,
"goals_scored":24,
"goals_against":9
},
"total":{
"goal_difference":"+37",
"points":60
},
"result":"Promotion - Premiership (Championship Group)",
"points":60,
"recent_form":"WWWDW",
"status":"same"
}
]}
I have a way to associate the team_ID with specific team data from the API. What I'm trying to do is to make the teams in my Standings tables clickable, so when the user clicks on a team, they can go to a page where I display data for that specific team (the info would, of course, be extracted from the API). Now, I'm developing the website on top of WordPress, and I'm wondering how I can auto-generate pages for every team, so that site users can go to specific team pages directly from the Standings tables.
It's not convenient for me to manually create pages with a piece of code and upload them to the FTP server for each team, because there'll be so many teams (eventually thousands of teams), and it's definitely not feasible.
Can anyone please guide me on the way to auto-create pages for the teams so my team links don't return 404 Not Found pages? An important point is that I'm using JavaScript to manipulate the data.
I'm using the team_id property in the API to create my team links, but I don' know how to use my team_id to automatically create team pages on my WordPress site.
Please help if you can. :)
You can use ajax method on click team-id, in ajax call function, check, is this team-id exists or not, if exists redirect to respective page or not create a new page and display require information.
I have worked same type project, I am using CPT method for the game and used the custom taxonomy for team and player.

Access React app from other React app on the same page

I have a situation where I have written the front-end of my website as micro-services. Every functional section of my website is a react-habitat application bundled with css and html. I have done this so I can re-use these sections wherever I want on the site.
My problem is however in the checkout area I need to pass the customerId from the login application (on successful response) into the delivery address application so I can bring back the default address. I don't want to create a new application and duplicate the code for login and delivery address applications because I will need to use them elsewhere on the website.
So my question is. How can I pass this customerId from my login application to my delivery address application without a page reload? Is there a way to access a react application method from outside of that react application?
Login click handler:
clickHandler(e) {
e.preventDefault();
let payload = {"email":this.props.email, "password":this.props.password};
AuthenticationApp.login(payload).then((response) => {
if (this.props.referrer === "checkout") {
$("#checkout_login-register-tab").addClass("done");
$("#checkout_login-delivery-tab").removeClass("todo");
// Temporary hack to reload page until we can pass new user session into delivery/billing react app from here.
location.reload();
}
this.props.updateParentState({isLoggedIn: true});
})
.catch(function(response){
console.log("res2 - catch", response)
});
}
I have forced a page reload here so the delivery address application re-renders and picks up customerId from the service. I need a way to re-render the delivery application at this stage (from this separate application), either by accessing a function somehow or forcing the application to reset? Is this possible or is there a workaround other than a page reload?
You can do something like this:
loadDeliveryApp(data) {
ReactDOM.render(<DeliveryApp data={data} />, document.getElementById('delivery-app'))
ReactDOM.unmountComponentAtNode(document.getElementById("login-app"))
}
clickHandler(e) {
e.preventDefault()
// your login stuff
loadDeliveryApp(data)
}
Here is the working demo.
https://codepen.io/madhurgarg71/pen/dzgxMd
I'm the original creator of React Habitat.. all your applications can talk to each other via either a flux observer or something like redux.
I suggest flux for react habitat normally as the individual stores keep it light weight instead of one BIG store in redux where you might have dead code in a habitat situation.
You will need to create one common store that both your applications import so that if one changes it, the other will get the update. I can put a demo together later if that helps.

Communicating with a web widget-Meteor, React, Node

I'm building a chat dashboard and widget with which a customer should be able to put the widget into their page. Some similar examples would be Intercom or Drift.
Currently, the "main" application is written in Meteor.js (it's front end is in React). I've written a <Widget /> component and thrown it inside a /widget directory. Inside this directory, I also have an index.jsx file, which simply contains the following:
import React from 'react';
import ......
ReactDOM.render(
<Widget/>,
document.getElementById('widget-target')
);
I then setup a webpack configuration with an entry point at index.jsx and when webpack is run spits out a bundle.js in a public directory.
This can then be included on another page by simply including a script and div:
<script src="http://localhost:3000/bundle.js" type="text/javascript"></script>
<div id="widget-target"></div>
A few questions:
What is wrong with this implementation? Are their any security issues to be aware of? Both the examples linked earlier seem make use of an iframe in one form or another.
What is the best way to communicate with my main meteor application? A REST API? Emit events with Socket.io? The widget is a chat widget, so I need to send messages back and forth.
How can I implement some sort of unique identifier/user auth for the user and the widget? Currently, the widget is precompiled.
1 What is wrong with this implementation? Are their any security issues to be aware of? Both the examples linked earlier seem make use of an iframe in one form or another.
As #JeremyK mentioned, you're safer within an iFrame. That being said, there's a middle route that many third parties (Facebook, GA, ...) are using, including Intercom:
ask users to integrate your bundled code within their webpage. It's then up to you to ensure you're not introducing a security vulnerability on their site. This code will do two things:
take care of setting up an iframe, where the main part of your service is going to happen. You can position it, style it etc. This ensure that all the logic happening in the iframe is safe and you're not exposed.
expose some API between your customer webpage and your iframe, using window messaging.
the main code (the iframe code) is then loaded by this first script asynchronously, and not included in it.
For instance Intercom ask customers to include some script on their page: https://developers.intercom.com/docs/single-page-app#section-step-1-include-intercom-js-library that's pretty small (https://js.intercomcdn.com/shim.d97a38b5.js). This loads extra code that sets the iFrame and expose their API that will make it easy to interact with the iFrame, like closing it, setting user properties etc.
2 What is the best way to communicate with my main meteor application? A REST API? Emit events with Socket.io? The widget is a chat widget, so I need to send messages back and forth.
You've three options:
Build your widget as an entire Meteor app. This will increase the size of the code that needs to be loaded. In exchange for the extra code, you can communicate with your backend through the Meteor API, like Meteor.call, get the reactivity of all data (for instance if you send a response to a user through your main Meteor application, the response would pop up on the client with no work to do as long as they are on the same database (no need to be on the same server)), and the optimistic UI. In short you've all what Meteor offers here, and it's probably going to be easier to integrate with your existing backend that I assume is Meteor.
Don't include Meteor. Since you're building a chat app, you'll probably need socket.io over a traditional REST API. For sure you can do a mix of both
Use Meteor DDP. (it's kind of like socket.io, but for Meteor. Meteor app use that for all requests to the server) This will include less things that the full Meteor and probably be easier to integrate to your Meteor backend than a REST API / socket.io, and will be some extra work over the full Meteor.
3 How can I implement some sort of unique identifier/user auth for the user and the widget?
This part should probably do some work on the customer website (vs in your iframe) so that you can set cookies on his page, and send that data to your iframe that's gonna talk to your server and identify the user. Wether you use artwells:accounts-guest (that's based on meteor:accounts-base) is going to depend on wether you decide to include Meteor in your iframe.
If you don't have Meteor in your iframe, you can do something like:
handle user creation yourself, by simply doing on your server
.
const token = createToken();
Users.insert({ tokens: [token] });
// send the token back to your iframe
// and set is as a cookie on your customer website
then for each call to your server, on your iframe:
.
let token;
const makeRequest = async (request) => {
token = token || getCookieFromCustomerWebsite();
// pass the token to your HTTP / socket.io / ... request.
// in the header of whatever
return await callServer(token, request);
};
in the server have a middleware that sets the user. Mine looks like:
.
const loginAs = (userId, cb) => {
DDP._CurrentInvocation.withValue(new DDPCommon.MethodInvocation({
isSimulation: false,
userId,
}), cb);
};
// my middleware that run on all API requests for a non Meteor client
export const identifyUserIfPossible = (req, res, next) => {
const token = req.headers.authorization;
if (!token) {
return next();
}
const user = Users.findOne({ tokens: token });
if (!user) {
return next();
}
loginAs(user._id, () => {
next();
// Now Meteor.userId() === user._id from all calls made on that request
// So you can do Meteor.call('someMethod') as you'd do on a full Meteor stack
});
};
Asking your customers to embed your code like this doesn't follow the principles of Security by Design.
From their point of view, you are asking them to embed your prebundled code into their website, exposing their site up to any hidden security risks (inadvertent or deliberately malicious) that exist in your code which would have unrestricted access to their website's DOM, localstorage, etc.
This is why using an iframe is the prefered method to embed third party content in a website, as that content is sandboxed from the rest of it's host site.
Further, following the security principle of 'Least Privilege' they (with your guidance/examples) can set the sandbox attribute on the iframe, and explicitly lockdown via a whitelist the privileges the widget will have.
Loading your widget in an iframe will also give you more flexibility in how it communicates with your servers. This could now be a normal meteor client, using meteor's ddp to communicate with your servers. Your other suggestions are also possible.
User auth/identification depends on the details of your system. This could range from using Meteor Accounts which would give you either password or social auth solutions. Or you could try an anonymous accounts solution such as artwells:accounts-guest.
html5rocks article on sandboxed-iframes

Categories

Resources