I am using Mobx and mobx-model for state management in my React app. I am not doing server-side render as of now. But, I have a scenario where I need to use my model in the server side.
An example model in my project is shown below.
import { API, BaseModel } from "mobx-model";
class UserModel extends BaseModel {
...
static loadAll() {
...
}
}
The above model works fine in the client (in the browser). But, I have a scenario where I need to call the loadAll method from the server.
If I require this model from the server side as follows, I get an error.
const { UserModel } = require("../../src/models/models");
The error message is:
SyntaxError: Unexpected token import
Any idea how I can fix this to work on the server side?
I found a solution to this problem. Instead of requiring the UserModel, I could require the API from mobx-model as follows:
const { API } = require("mobx-model");
I could use the API from the server without much code changes. I have to rewrite a little bit of loadAll logic again in the server. That works for me for now.
Related
Azure App Insights recommends using their react plugin for SPAs. https://learn.microsoft.com/en-us/azure/azure-monitor/app/javascript-react-plugin
In the documented example, they manually create a browser history to pass to the extension.
// AppInsights.js
import { ApplicationInsights } from '#microsoft/applicationinsights-web';
import { ReactPlugin } from '#microsoft/applicationinsights-react-js';
import { createBrowserHistory } from 'history';
const browserHistory = createBrowserHistory({ basename: '' });
const reactPlugin = new ReactPlugin();
const appInsights = new ApplicationInsights({
config: {
instrumentationKey: 'YOUR_INSTRUMENTATION_KEY_GOES_HERE',
extensions: [reactPlugin],
extensionConfig: {
[reactPlugin.identifier]: { history: browserHistory }
}
}
});
appInsights.loadAppInsights();
export { reactPlugin, appInsights };
However, I'm using react router 6, which I assume creates its own browser history. The documentation does make reference to the react router documentation, however, this is for react router 5, which does expose the history directly.
With react router 6, what would be the right way to access to the history created by the router?
This issue is discussed in great length on the following pull request [v6] Add <HistoryRouter> for standalone history objects. At a high-level, it should allow you to create a Router object with an external history object similar to the v5 documentation.
Currently, you best bet is to import the HistoryRouter component from the pull request into your own project so you can create and use an standalone history object for app insights.
I found the documentation through this question so thought it might be worth mentioning here that it has been updated to specifically address react-router v6:
https://learn.microsoft.com/en-us/azure/azure-monitor/app/javascript-react-plugin
For react-router v6 or other scenarios where router history is not
exposed, appInsights config enableAutoRouteTracking can be used to
auto track router changes
As per this github issue, page view duration data is lost as a result.
In Next.js you have the option of server side rendering (SSR) or static site generation (SSG). Throughout the Next.js docs and community, SSG is recommended over SSR for performance reasons.
I have a Next.js build that uses SSG throughout the application, using getStaticProps() etc. to generate the content/pages at build time by integrating with an external CMS (Prismic). I prefer this because as mentioned it gives a performance boost and also most of the codebase can then use the same data-fetching strategy (at build time).
However, some of these pages need to be protected - meaning they should only be accessed by authenticated users. We are using Auth0 to generate a JWT token and have a React context provider save the status of the user (logged in or not) after validating the token in an API call.
But, I am struck that I don't seem to have a good way to protect SSG pages with this token. The recommended way here strikes me as odd because as far as I can tell this is a client-side redirect that could be manipulated by the client (for example - the client could manipulate it's local state/context or else tamper with whatever is returned from notloggedincondition) to show the static content or otherwise short-circuit the redirect.
For reference, here is a paste of that code:
import {useEffect} from 'react'
import {useRouter} from 'next/router'
export async function getStaticProps() {
return {
props: {
hello: 'Hello World'
}
}
}
export default (props) => {
const router = useRouter()
useEffect(() => {
if(notloggedincondition) {
router.push('/login')
}
}, [])
return <h1>Rest of the page</h1>
}
Note the <h1>Rest of the page</h1> could still be accessed by manipulating the client... so I want to secure the SSG at the request/response level and do a server side redirect (if need be), or something like that.
Short of something like this, is there no way to securely protect a SSG page without having to rely on client-side routing? Do I need to SSR the content even though it is no different really from the rest of the content, save for the requirement that only authenticated users can see it?
Perhaps I am missing something obvious, but it seems to me that even with a static site there should be a way to protect it without relying on client side routing. That is to say, it does not seem intrinsic to the concept of a statically generated site that every page must be public, so I'm wondering about a way to do this in Next.js that is secure.
The best way I could find to accomplish this is via SWR fetches, statically generating a skeleton of the page with initial unprotected static data and then hydrating it with the refresh, if the refresh returns content.
This does require that you move logic gathering data for the protected page behind an API or CMS (anything which would clear your view of permissions), and converting existing routes to use API calls isn't a trivial task, so YMMV.
Important note: your redirect would still need to be client-side, but you can avoid having anything protected shown to an unauthorized user as that'd still be controlled at the server level. Since your biggest concern appears to be a user actively trying to compromise content by manipulating code, this seems to meet your risk remediation criteria (they would still be unable to access protected content).
Example page code:
import {useEffect} from 'react'
import {useRouter} from 'next/router'
import useSWR from 'swr'
export async function getStaticProps() {
return {
props: {
hello: 'Hello World'
}
}
}
export default (props) => {
const router = useRouter()
// Access the protected content via an API route,
// provide the initial unprotected static content via the initialData param
const { data } = useSWR('/api/protected-content', fetcher, { initialData: props })
useEffect(() => {
if(notloggedincondition) {
router.push('/login')
}
}, [])
return <h1>{ data.hello }</h1>
}
Then, an example API implementation at pages/api/protected-content:
export default async function ProtectedContent(req, res) {
// Get a session object based on request cookies
let session = await initUserSession(req, res);
// If a session is available, return the protected content
if (session.props.userSession) {
return res.status(200).json({hello: 'This is protected content'});
} else {
return res.status(401).send("UNAUTHENTICATED");
}
}
Next js now has middleware that can redirect request based on the context.
Encountered the same problem at work, and opted for a custom server that sits in front of Next.js.
We're using Express, but any Node framework would work really. This lets us move authentication concerns to a separate server framework, which in turn forwards the requests to Next.js.
https://nextjs.org/docs/advanced-features/custom-server
I have an Angular 7 site, that works fine without server-side rendering (SSR). I host it using Google's Firebase hosting. I now want to setup SSR. I followed this guide and got it to build and deploy to Firebase Hosting and Functions.
However, the site does not load and the Functions logs include this entry:
ERROR ReferenceError: document is not defined
at new CssKeyframesDriver (/user_code/node_modules/#angular/animations/bundles/animations-browser.umd.js:4246:26)
at instantiateSupportedAnimationDriver (/user_code/node_modules/#angular/platform-browser/bundles/platform-browser-animations.umd.js:412:88)
at _callFactory (/user_code/node_modules/#angular/core/bundles/core.umd.js:19867:24)
at _createProviderInstance (/user_code/node_modules/#angular/core/bundles/core.umd.js:19825:30)
at resolveNgModuleDep (/user_code/node_modules/#angular/core/bundles/core.umd.js:19786:25)
at _createClass (/user_code/node_modules/#angular/core/bundles/core.umd.js:19854:72)
at _createProviderInstance (/user_code/node_modules/#angular/core/bundles/core.umd.js:19822:30)
at resolveNgModuleDep (/user_code/node_modules/#angular/core/bundles/core.umd.js:19786:25)
at _callFactory (/user_code/node_modules/#angular/core/bundles/core.umd.js:19873:71)
at _createProviderInstance (/user_code/node_modules/#angular/core/bundles/core.umd.js:19825:30)
Any ideas of what is wrong? I can provide code snippets or individual read access to the repo if requested.
Server side rendering probably means serving HTML from Node.js w Express.js. In the Firebase suite of products, this is accomplished using Cloud Functions for Firebase's HTTP triggers.
You can have a look at Firebase Samples on Github. This is a relatively advanced implementation so proceed as long as you are strong in JavaScript, HTML, and CSS (not to mention Angular).
Angular SSR is trying to run angular animations but it's not able to find any document variable(there is no document on server-side).
You can solve this by conditionally executing your code with the help of isPlatformBrowser and isPlatformServer.
example
Import this
import { Component, PLATFORM_ID } from '#angular/core';
import { isPlatformBrowser, isPlatformServer } from '#angular/common';
User like this
constructor(#Inject(PLATFORM_ID) platformId: string){
this.isBrowser = isPlatformBrowser(platformId);
this.isServer = isPlatformBrowser(platformId);
if(this.isBrowser){
//Do something on the browser
}
if(this.isServer){
//Do something on the server
}
}
Does Loopback4 provide a way to generate an Open Api Spec from decorated models and controllers?
If not, is there a way to access controllers and models metadata at runtime?
Noticed just now that the server object exposes the getApiSpec method which returns an OpenApiSpec object.
export class MyController {
constructor(
#inject('application.instance') private app: Application,
#inject('rest.http.request') private req: ServerRequest) { }
#get('/spec')
async api(): Promise<OpenApiSpec> {
const server = await this.app.getServer(RestServer);
return server.getApiSpec();
}
}
EDIT: This solution doesn't solve the problem completely
The api spec returned by server.getApiSpec() doesn't contain information about the models.
After you started the LoopBack 4 application (assuming you did not change the REST server port), go to http://localhost:3000/openapi.json, you'll get the OpenAPI spec.
I'm trying to get google calendar events into my React Redux app.
I've tried using googleapis and google-auth-library but webpack is throwing errors because googleapis was built to run server side and bundle.js is referenced from client.
So I've read a few forums about these errors and they all point to using Google's js client library instead.
I understand how to implement this in a java or php app (I'm old... 35 ;) but I'm new to React Redux and I'm looking for the best way to implement this.
I'm trying to fetch the events from my calendar in my actions.js. I tried including <script src="https://apis.google.com/js/api.js"></script> in my html header and then using gapi.load() from actions.js. I also tried creating a api.js file and referencing that with require('./api'). I also tried to use the cli commands from the Node.js Quickstart guide to get an access_token and then just use axios to call Google API directly but I'm getting a 403. I'm thinking I'm just not providing the proper headers but that wouldn't be best practice anyway.
My question is basically how do I reference Google's js client library from my actions.js file while adhering to Redux standards?
You're on the right track by including the official Google client API in the HTML header. It's less than ideal -- it would be nice if Google provided the (browser) client API as an npm module that you could import. But they don't (that I see), so I think what you're doing is fine.
Then there's the question of "how do I use it in a way that's React/Redux friendly?" Redux is a mechanism for managing the state of your application. The Google API is not part of your application (though what you do with it may inform the state of your application).
It's easy to verify that you have access to the Google API: you can just make a call from the componentDidMount method of one of your components, and do a console log:
class MyComp extends React.Component {
componentDidMount() {
// this is taken directly from Google documentation:
// https://developers.google.com/api-client-library/javascript/start/start-js
function start() {
// 2. Initialize the JavaScript client library.
gapi.client.init({
'apiKey': 'YOUR_API_KEY',
// clientId and scope are optional if auth is not required.
'clientId': 'YOUR_WEB_CLIENT_ID.apps.googleusercontent.com',
'scope': 'profile',
}).then(function() {
// 3. Initialize and make the API request.
return gapi.client.request({
'path': 'https://people.googleapis.com/v1/people/me',
})
}).then(function(response) {
console.log(response.result);
}, function(reason) {
console.log('Error: ' + reason.result.error.message);
});
};
// 1. Load the JavaScript client library.
gapi.load('client', start);
},
}
If you don't see what you expect on the console, somehow gapi isn't getting loaded as you expect. If that happens, you'll have a more specific question you can ask!
If you do get a response, you now know how to call GAPI...but then how to make use of it in a Redux-friendly way?
When you make a GAPI call, you probably want to modify your application's state in some way (otherwise why would you be doing it?) For example, you might invoke the auth flow, and when GAPI returns success, your application state now has loggedIn: true or similar (possibly with lots of other state changes). Where you make the GAPI call is up to you. If you want to do it when the component loads, you should do it in componentDidMount. You also may commonly be making the GAPI call in response to a user action, such as clicking on a button.
So the typical flow would be something like this:
// either in componentDidMount, or a control handler, usually:
someGapiCall()
.then(result => {
this.props.onGapiThing(result.whatever)
})
Where this.props.onGapiThing is a function that dispatches an appropriate action, which modifies your application state.
I hope this overview helps...feel free to follow up with more specific questions.
Can you try this library which I used to load external libraries and modules in my React app when I couldn't find a NPM module for it:
https://github.com/ded/script.js/
So your code will be like this:
import $script from 'scriptjs';
$script('https://apis.google.com/js/api.js', function () {
//Put your google api functions here as callback
});
I'm going to answer my own question despite some very good correct answers.
#MattYao answered my actual question of how to get a js script available for reference in my actions.js file.
#Ethan Brown gave a very detailed answer that showed some excellent flow possibilities.
#realseanp changed the scope but a valid answer.
I tried all of the above and they worked.
So I'm not sure what I was doing wrong but I was finally able to access the gapi object from actions.js by just adding <script src="https://apis.google.com/js/api.js"></script> to my index head.
I'm using pug so it looks like this:
doctype
html
head
title MyTitle
link(rel='stylesheet' href='/static/css/main.css')
link(rel='stylesheet' href='/static/css/react-big-calendar.css')
script(src='https://apis.google.com/js/api.js' type='text/javascript')
body
div(id='app')
script(src='/static/bundle.js' type='text/javascript')
Here is my component file:
import React from 'react'
import BigCalendar from 'react-big-calendar';
import moment from 'moment';
import { connect } from 'react-redux'
import { fetchEvents } from '../actions/actions'
BigCalendar.momentLocalizer(moment);
#connect((store) => {
return {
events: store.events.events
}
})
export default class Calendar extends React.Component
{
componentWillMount()
{
this.props.dispatch(fetchEvents())
}
render()
{
return (
<div>
<BigCalendar
events={this.props.events}
startAccessor='startDate'
endAccessor='endDate'
style={{height: 800}}
/>
</div>
)
}
}
And then I was able to get this working in my actions.js file
export function fetchEvents()
{
return (dispatch) =>
{
function start()
{
// 2. Initialize the JavaScript client library.
gapi.client.init({
'apiKey': API_KEY,
// clientId and scope are optional if auth is not required.
'clientId': CLIENT_ID,
'scope': 'profile',
}).then(function() {
// 3. Initialize and make the API request.
return gapi.client.request({
'path': 'https://www.googleapis.com/calendar/v3/calendars/MY_EMAIL#gmail.com/events?timeMax=2017-06-03T23:00:00Z&timeMin=2017-04-30T00:00:00Z',
})
}).then( (response) => {
let events = response.result.items
dispatch({
type: 'FETCH_EVENTS_FULFILLED',
payload: events
})
}, function(reason) {
console.log(reason);
});
};
// 1. Load the JavaScript client library.
gapi.load('client', start)
}}
I had to make my calendar public to access it this way. So now I'm going to work on the oauth2 stuff :/
I would load all the google stuff in my index file before i loaded my webpack bundle (Option 1) . Then I would use redux sagas to call the google apis. Loading the google code before your webpack bundle will ensure everything is ready to go when you call the api from the saga
Try this package.
It looks like it is updated.
https://github.com/QuodAI/tutorial-react-google-api-login