I'm trying to implement a payments system in my ReactJS app that requires server-side code.
I have several questions:
How do you connect a ReactJS app so it can communicate with server-side code?
How would you set up a function in the server-side code?
How would you call that function from a component in a ReactJS app?
For reference, I'm trying to integrate Stripe subscriptions. They give server-side code examples for Node, PHP, etc.
FYI: I am not trying to set up server-side rendering. When you search for server-side code in reference to ReactJS, that's just about all that comes up.
EDIT: I'm particularly interested in a NodeJS solution. I'm also using Webpack.
Just in case, it is helpful to you... I have a React UI that triggers video processing on a Django backend (I mainly use GraphQL through Apollo Client to trigger my server side functions and REST framework when file transfers are involved).
Is REST an option for you?
The middleware I use for file transfers for example:
const SERVER_URL = process.env.SERVER_URL;
const fileTransferApi = (payload) => {
const { authenticated, token, endpoint, body, contentType, method } = payload;
let config = {};
if (authenticated) {
if (token) {
config = {
method,
headers: {
'Content-Type': contentType,
Authorization: `Bearer ${token}`
},
body
};
} else {
throw new Error('No token saved!');
}
}
return fetch(`${SERVER_URL}/api/rest/v1/${endpoint}`, config)
.then((response) =>
response.text().then((text) => ({ text, response }))
).then(({ text, response }) => {
if (!response.ok) {
return Promise.reject(text);
}
return text;
}).catch((err) => console.log(err));
};
export const FILE_TRANSFER_API = Symbol('FILE_TRANSFER_API');
export default () => (next) => (action) => {
const fileTransferApiAction = action[FILE_TRANSFER_API];
if (typeof fileTransferApiAction === 'undefined') {
return next(action);
}
const { payload, types } = fileTransferApiAction;
const [, successType, errorType] = types;
return fileTransferApi(payload).then(
(response) =>
next({
type: successType,
payload: {
text: response,
message: 'ok'
}
}),
(error) => next({
type: errorType,
payload: {
error: error.message || 'There was an error.'
}
})
);
};
My store (I use Redux):
import { createStore, compose, applyMiddleware } from 'redux';
import { routerMiddleware } from 'react-router-redux';
import ReduxThunk from 'redux-thunk';
import ApolloClientSingleton from '../network/apollo-client-singleton';
import fileTransferApi from '../middlewares/fileTransferApi';
import reducer from './reducers';
export default class Store {
constructor(history, initialState = {}) {
this.data = createStore(
reducer,
initialState,
compose(
applyMiddleware(
fileTransferApi,
ReduxThunk.withExtraArgument(ApolloClientSingleton),
routerMiddleware(history),
ApolloClientSingleton.middleware()
),
typeof window === 'object' && typeof window.devToolsExtension !== 'undefined'
? window.devToolsExtension() : (f) => f
)
);
}
}
In my actions:
export const windowsDownload = (authenticated, token) => ({
[FILE_TRANSFER_API]: {
types: [WINDOW_DOWNLOAD_REQUEST, WINDOW_DOWNLOAD_SUCCESS, WINDOW_DOWNLOAD_FAILURE],
payload: {
endpoint: 'file_transfer/download/windows',
contentType: 'text/csv',
method: 'get',
body: null,
authenticated,
token
}
}
});
This REST setup enables me to send requests (POST video, GET csv...) from my React UI to my Django server. Can't you set up some REST calls between your app and your server?
Related
So I'm creating authentication logic in my Next.js app. I created /api/auth/login page where I handle request and if user's data is good, I'm creating a httpOnly cookie with JWT token and returning some data to frontend. That part works fine but I need some way to protect some pages so only the logged users can access them and I have problem with creating a HOC for that.
The best way I saw is to use getInitialProps but on Next.js site it says that I shouldn't use it anymore, so I thought about using getServerSideProps but that doesn't work either or I'm probably doing something wrong.
This is my HOC code:
(cookie are stored under userToken name)
import React from 'react';
const jwt = require('jsonwebtoken');
const RequireAuthentication = (WrappedComponent) => {
return WrappedComponent;
};
export async function getServerSideProps({req,res}) {
const token = req.cookies.userToken || null;
// no token so i take user to login page
if (!token) {
res.statusCode = 302;
res.setHeader('Location', '/admin/login')
return {props: {}}
} else {
// we have token so i return nothing without changing location
return;
}
}
export default RequireAuthentication;
If you have any other ideas how to handle auth in Next.js with cookies I would be grateful for help because I'm new to the server side rendering react/auth.
You should separate and extract your authentication logic from getServerSideProps into a re-usable higher-order function.
For instance, you could have the following function that would accept another function (your getServerSideProps), and would redirect to your login page if the userToken isn't set.
export function requireAuthentication(gssp) {
return async (context) => {
const { req, res } = context;
const token = req.cookies.userToken;
if (!token) {
// Redirect to login page
return {
redirect: {
destination: '/admin/login',
statusCode: 302
}
};
}
return await gssp(context); // Continue on to call `getServerSideProps` logic
}
}
You would then use it in your page by wrapping the getServerSideProps function.
// pages/index.js (or some other page)
export const getServerSideProps = requireAuthentication(context => {
// Your normal `getServerSideProps` code here
})
Based on Julio's answer, I made it work for iron-session:
import { GetServerSidePropsContext } from 'next'
import { withSessionSsr } from '#/utils/index'
export const withAuth = (gssp: any) => {
return async (context: GetServerSidePropsContext) => {
const { req } = context
const user = req.session.user
if (!user) {
return {
redirect: {
destination: '/',
statusCode: 302,
},
}
}
return await gssp(context)
}
}
export const withAuthSsr = (handler: any) => withSessionSsr(withAuth(handler))
And then I use it like:
export const getServerSideProps = withAuthSsr((context: GetServerSidePropsContext) => {
return {
props: {},
}
})
My withSessionSsr function looks like:
import { GetServerSidePropsContext, GetServerSidePropsResult, NextApiHandler } from 'next'
import { withIronSessionApiRoute, withIronSessionSsr } from 'iron-session/next'
import { IronSessionOptions } from 'iron-session'
const IRON_OPTIONS: IronSessionOptions = {
cookieName: process.env.IRON_COOKIE_NAME,
password: process.env.IRON_PASSWORD,
ttl: 60 * 2,
}
function withSessionRoute(handler: NextApiHandler) {
return withIronSessionApiRoute(handler, IRON_OPTIONS)
}
// Theses types are compatible with InferGetStaticPropsType https://nextjs.org/docs/basic-features/data-fetching#typescript-use-getstaticprops
function withSessionSsr<P extends { [key: string]: unknown } = { [key: string]: unknown }>(
handler: (
context: GetServerSidePropsContext
) => GetServerSidePropsResult<P> | Promise<GetServerSidePropsResult<P>>
) {
return withIronSessionSsr(handler, IRON_OPTIONS)
}
export { withSessionRoute, withSessionSsr }
I am trying to import a function from an external file I have, which contains a promise.
The resolution of the promise is what should be returned, but I get an error message:
The requested module './functions.js' does not provide an export named 'getOkapiToken'
The POST request works fine when I run it directly in the server, but I need it in a separate file as it will be used by several different files in the future.
This is also the first time I'm using promises, so I'm not sure I handled it properly (though I can't get any errors registered until I deal with the first one).
The functions.js file is built as follows:
import post from 'axios';
export function getOkapiToken(url, user, password) {
//Get username and password for API
const auth = {
"username": user,
"password": password
};
//Create headers for POST request
const options = {
method: 'post',
data: auth,
headers: {
'Content-Type': 'application/json',
'Content-Length': auth.length,
'X-Okapi-Tenant': 'diku'
}
};
//Make API post call
post(url+':9130/authn/login', options)
.then(response => {
return(response.headers['x-okapi-token'])
}).catch((err) => {
return(`There was an error 2: ${err}`)
})
}
And I try to import it as follows:
import { getOkapiToken } from './functions3.js'
import settings from './jsons/config.json';
let OkapiKey = await new Promise((resolve,reject) => {
//Call function to make API post call
let keytoken = getOkapiToken(settings.url,settings.userauth,settings.passauth)
console.log(`There was an error: ${keytoken}`)
if (keytoken.length == 201) {
resolve(keytoken)
} else {
reject('There was an error')
}
})
OkapiKey.then((data) => {
console.log(data)
})
.catch((err) => {
console.error('I have an error:'+err.code);
})
There are three ways to handle asynchronous task in Javascript and wit Node.JS
Pass in a Callback to run in the asynchronous code
Use a Promise that will either resolve or reject the promise
Use the async keyword in front of the function and place await in front of the asynchronous code.
With that said I was able to get the code to work by running a simple node server and I modified your code just a bit to get a response back from the server.
index.js
const { getOkapiToken } = require('./functions.js')
const settings = require('./settings.json')
var OkapiKey = getOkapiToken(settings.url,settings.userauth,settings.passauth)
OkapiKey
.then((data) => {
console.log('I have data'+ data.toString())
})
.catch((err) => {
console.error('I have an error:'+err.code);
})
functions.js
const post = require('axios');
const getOkapiToken = (url, user, password) =>
new Promise(function (resolve, reject) {
//Get username and password for API
const auth = {
"username": user,
"password": password
};
//Create headers for POST request
const options = {
method: 'post',
data: auth,
headers: {
'Content-Type': 'application/json',
'Content-Length': auth.length,
'X-Okapi-Tenant': 'diku'
}
};
post('http://localhost:3000/', options)
.then(response => {
resolve(response.data)
// if (response.headers['x-okapi-token']) {
// resolve(response.headers['x-okapi-token']);
// } else {
// reject((error));
// }
}).catch((err) => {
console.error('Response Error:'+err)
})
})
exports.getOkapiToken = getOkapiToken;
I am creating a website using the MERN stack however I don't know how to get data to the frontend that needs authorization from the backend and I tried to console log the problem and it shows me the HTML of my login page even though I am logged in. Any will be appreciated thank you so much.
My backend code:
router.get("/questions", ensureAuthenticated, (req, res) => {
math = Math.floor(Math.random() * 3) + 1;
Security.findOne({
user: req.user.id
}, (err, user) => {
if (err) {
console.log(err);
}
if (math === 1) {
res.send({
question: user.firstQuestion
});
} else if (math === 2) {
res.send({
question: user.secondQuestion
});
} else {
res.send({
question: user.thirdQuestion
});
}
});
});
My Frontend code:
class QuestionForm extends Component {
constructor(props) {
super(props);
this.state = {
data: ''
}
}
componentDidMount() {
axios.get("http://localhost:5000/users/questions")
.then((res) => {
this.setState({
data: res.data
});
}).catch((err) => console.log(err));
}
render() {
return <h1 > {
this.state.data
} < /h1>
}
}
a lot of changes should be made.
you never want to use the port in your Axios request
add to you package.json an proxy attribute
"proxy": "http://localhost:5000"
then you can change your axios get to
axios.get("/users/questions")
best practice when using autorization is to add to axios interceptors
follow this thread :
How can you use axios interceptors?
and also here is an example for using authorization with JWT token
const tokenHandler = axios.create();
tokenHandler.interceptors.request.use(config => {
const token = localStorage.getItem("token");
if (token) {
config.headers["Authorization"] = token;
}
return config;
});
export default tokenHandler;
let's say you create a token on the login page and store it inside your local storage.
now you can import the token handler and your request should look something like this :
import {tokenHandler} from '<TOKEN HANDLER PATH>'
..
..
tokenHandler.get("/users/questions")
.then((res)=>{
this.setState({data:res.data});
}).catch((err)=>console.log(err));
I had a question that doesn't seem to be answered anywhere.
I am running tests from within my Express.js api. I set up a page that has a button and a field to enter a keyword intended to be used during a testcafe test. My endpoint I set up is /testcafe. But after sending a post request to /testcafe, there is a long delay while test runs and so my question is what is the best next step besides hanging?
Also, can my post request body, which contains the keyword, be directly used in a test like this? Keep in mind it's this pattern:
frontend -> POST request -> Express server -> /testcafe endpoint - test
My problem is after it reaches test, I currently have it attempting to call fetch from within the request logger. Is this right?
import { ClientFunction, Selector } from 'testcafe';
import { RequestLogger, RequestHook } from 'testcafe';
import zlib from 'zlib';
import fetch from 'isomorphic-unfetch';
const url = 'https://www.mysitetesturl.com/page';
class MyRequestHook extends RequestHook {
constructor (requestFilterRules, responseEventConfigureOpts) {
super(requestFilterRules, responseEventConfigureOpts);
}
onRequest (e) {
console.log('in onRequest!')
console.log('========================')
console.log('Request Body')
let buf = e._requestContext.reqBody
console.log(buf.toLocaleString())
}
onResponse (e) {
let buf = Buffer(e.body)
let unzippedBody = Buffer(zlib.gunzipSync(buf))
let payload = unzippedBody.toLocaleString()
fetch('http://myapiipaddress/api/testcafe',
method: 'PUT',
body: JSON.stringify(payload)
)
.then((err, doc) => {
if(err) {
console.log(err)
} else {
console.log(doc)
}
})
}
}
const myRequestHook = new MyRequestHook({
url: url,
method:'get'},
{
includeHeaders: true,
includeBody: true
}
);
fetch('http://myapiipaddress/api/testcafe',
method: 'GET'
)
.then((err, doc) => {
if(err) {
console.log(err)
} else {
fixture`myfixture`
.page(doc.url)
.requestHooks(myRequestHook);
test(`mytest`, async t => {
const inputField = Selector('input');
await t
await t
.wait(5000)
.typeText(inputField, doc.text)
.wait(5000)
}
);
}
})
According to your scheme, you need to organize your code in a different way:
const createTestCafe = require('testcafe');
....
// Choose the necessary body parser for express application
// https://github.com/expressjs/body-parser
app.use(bodyParser.urlencoded({ extended: true }));
...
app.post('/', function (req, res) {
createTestCafe('localhost', 1337, 1338, void 0, true)
.then(testcafe => {
const runner = testcafe.createRunner();
return runner
.src('/tests')
.browsers('chrome')
.run();
})
.then(failedCount => {
testcafe.close();
res.end();
});
})
I'm brand new to this stack. I've seen quite a few other questions about this and have read the Thunk documentation but I can't stitch this together.
When I run the code below I get the error "Actions must be plain objects, use custom middleware for async actions" which is exactly the problem I'm trying to solve with Thunk.
My action looks like this:
src/actions/recipes.js
// this calls the API
function fetchApiGetRecipes() {
return fetch('https://mywebsite.com/endpoint/', {
method: 'GET',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + idToken
}
}).then((json) => {
dispatch({
type: 'RECIPES_REPLACE',
data: json
})
});
}
// this is passed into my container to use to refresh the recipe list
export function getRecipes() {
if (Firebase === null) return () => new Promise(resolve => resolve());
if (Firebase.auth().currentUser !== null) {
Firebase.auth().currentUser.getIdToken(/* forceRefresh */ true).then(function(idToken) {
// console.log(idToken);
return dispatch => new Promise(resolve => fetchApiGetRecipes(idToken) )
}).catch(function(error) {
// Handle error
});
} else {
console.log("Null user");
}
}
What is the correct syntax to use Thunk here and fix the error I'm getting when the app starts up?
EDIT: I create the store like this:
import { createStore, applyMiddleware, compose } from 'redux';
import { persistStore, persistCombineReducers } from 'redux-persist';
import storage from 'redux-persist/es/storage'; // default: localStorage if web, AsyncStorage if react-native
import thunk from 'redux-thunk';
import reducers from '../reducers';
// Redux Persist config
const config = {
key: 'root',
storage,
blacklist: ['status'],
};
const reducer = persistCombineReducers(config, reducers);
const middleware = [thunk];
const configureStore = () => {
const store = createStore(
reducer,
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__(),
compose(applyMiddleware(...middleware)),
);
const persistor = persistStore(
store,
null,
() => { store.getState(); },
);
return { persistor, store };
};
export default configureStore;
Your getRecipes function doesn't return a function in the if (Firebase.auth().currentUser !== null) clause.
You need to return a function where you are just doing
Firebase.auth().currentUser.getIdToken(/* forceRefresh */ true).then(function(idToken) {
// console.log(idToken);
return dispatch => new Promise(resolve => fetchApiGetRecipes(idToken) )
}).catch(function(error) {
// Handle error
});
The dispatch function (I assume is the intended one to return) is being returned in the then clause of the promise. That doesn't return the dispatch function to the outer method getRecipies. Hence the error
probably you forgot pass dispatch to func args?
// you use dispatch in this func
function fetchApiGetRecipes() {...}
// you forget pass
return dispatch => new Promise(resolve => fetchApiGetRecipes(idToken) )