I have an out javascript module, and I want to get the auth key from the vuex store
This is my code, that's seems that it work
import store from '../../../store';
class ExternalApi {
constructor() {
const that = this;
that.configuration = { apiKey: '', basePath: '/api/v2' };
that.authorizationHeader = function authorizationHeader() {
const cstore = store;
if (cstore && cstore.state && cstore.state.accessToken) {
that.configuration.apiKey = cstore.state.accessToken;
}
const localVarHeaderParams = { };
localVarHeaderParams.authorization = `Bearer ${that.configuration.apiKey}`;
return localVarHeaderParams;
};
this.list = function list(params) {
const localVarPath = `${that.configuration.basePath}/list`;
const localVarHeaderParams = that.authorizationHeader();
return axios....
};
}
I want to know 2 things:
Does it the way to connect to store from javascript or there is a better way for doing it
Does this code can make me some securities problems.
Answering your first question, when you want to use the store from outside Vue just use its API as you are doing though you don't really need to check for the store or the state to be initialized.
Regarding security, storing the authorization token in JS will be vulnerable to XSS attacks, the best way to avoid (or mitigate) them is implementing a robust Content Security Policy (CSP) and using a low (5-10min) time to live for your tokens.
On a side note, you don't need to rename store to cstore and if you use class methods instead of adding function properties in the constructor you wont need to juggle with this. Also note that storing the token in memory will not survive a page refresh so you may want to store it in sessionStorage.
Related
I am trying to clean up some code to remove redundancy and have some questions around best practices.
To keep it simple lets say I have 3 mobx stores PeopleStore, PlacesStore, and AuthStore.
All of these stores connect to a 3rd party api using fetch.
The AuthStore provides login functionality and has a property: #observable apiToken
There is duplicate logic in the other stores, setting the api url, the token, etc...
I tried creating the following file:
// ./api.js
const url = 'https://example.com'
const options = {
headers: {
Authorization: ??? (how do I access AuthStore.apiToken)
}
}
export const api = (endpoint) => {
fetch(url + endpoint, options)
}
Then in PeopleStore
import api from './api'
class PeopleStore {
getPeople() {
api('/people');
}
}
(This is just an example of the concept and not the actual code)
My quesions are:
Is this a good approach or are there better ways to go about this?
How do I access the apiToken inside api.js?
Am I correct in assuming that api is just a function and not a react component?
If so, can I still inject the AuthStore into the api()? (I believe I ran into issues with this)
My other thought would be to wrap all of the Stores in an HOC which provides this functionality, but again (I'm assuming) the Stores are not actual react components, does this cause any issues?
Another approach would be to just set your authorization header globally at the top level of your app. This would require you to install axios or something similar
For example, in your root index.js:
Get your api key, via process.env.apiToken or however you are getting it
Set the header:
axios.defaults.headers.common['Authorization'] = apiToken;
You can also set the base URL to help with the paths:
axios.defaults.baseURL = 'https://api.example.com';
This way you do not need to worry about the token, it will just be taken care of.
I have some data from a function in my app.component that I would like to store and use in other components. How can I achieve this?
Here is the function:
private async setupLocalUser(): Promise<boolean> {
try {
this.token = await jwtDecode(this.oauthService.getAccessToken());
//add user
this.userId = await this.userService.AddUser(this.token.email);
//add user session
this.userSessionId = await this.userSessionService.AddUserSession(this.token.email);
return true;
}
catch (Error) {
this.errorMessage = Error;
return false;
}
this.userSessionId is what I'd like to store for use as a parameter in different functions that are in other components. How can I achieve this?
Take a look at this post, I'm sure it'll be useful for your problem. As they say there, you could use a service or sessionStorage (more info here) to store the data so it can be accessed across the application.
I hope this helps you!
There is number of ways by using you can store/transfer data across the components. It totally depends on your situation/requirement. For example, you can use
Service's
web storage (Localstorage/ SessionStorage/ Cookies etc)
Global Variables
Variables as behaviour subject.
But I would suggest if the value is confidential you should store in a Global variable or using services. By doing so it's a bit hard for anyone to fetch data from the production build.
Let me know if you still have any query.
I have to validate that the user is logged in, using some token, that currently in the next example will already be set, for testing.
I have two options that I can think of.
Option 1
Do it on store's constructor:
export class MyStore {
#observable token = "sometoken";
#observable authenticated = false;
constructor() {
this.checkAuth();
}
#action
checkAuth() {
fetch("http://localhost:3001/validate/" + this.token)
.then(res => res.json())
.then(data => {
this.authenticated = data.validated;
});
// catch etc
}
}
Option 2:
Do it in my component's that uses the data, componentDidMount method.
Both of the ways work, but what is really the best practice to handle such state?
I would definitely go for the first option. If you don't always need the authentication - for example some parts are public - then just don't call this.checkAuth() in the store constructor. If all parts need authentication, then it looks good like this.
Option 2 should be avoided because that would make unnecessary roundtrips to the server to re-validate a token which was already validated. And in general MobX gives great tools to minimize the use of lifecycle methods and write a cleaner code.
Suppose I am writing an application in Redux and I am tasked to add logging using a 3rd party library. Its API is as follows:
function createLogger(token) {
// the logger has internal state!
let logCount = 0;
return {
log(payload) {
logCount++; // modify local state
fetch('/someapi', { // ship payload to some API
method: 'POST',
body: payload
});
}
};
}
I would then use the library something like this:
let logger = createLogger('xyz');
logger.log('foobar');
I definitely want to create the logger instance just once during application init. But then the question is: where do I store the logger instance?
First instict is to put it somewhere in the store. But is that a good idea? As I have demonstrated in the code the logger object is stateful, it stores a counter in the closure. I do not get a new instance like I would with an immutable object. As we know, state should only be modified via pure reducer functions.
Other possibilities are to create the instance somewhere in a redux middleware closure or just create a global variable, which is obviously evil in terms of testability.
Is there a best practice for this (I would think) rather common scenario?
Since you are using ES6 modules I would setup your logger as a module, export it, and import it wherever you plan to use it. I think logging from the actions is a solid plan, since it keeps the components unaware, and doesn't pollute the store with side-effects.
function createLogger(token) {
// the logger has internal state!
let logCount = 0;
return {
log(payload) {
logCount++; // modify local state
fetch('/someapi', { // ship payload to some API
method: 'POST',
body: payload
});
}
};
}
export default const logger = createLogger('xyz');
Your action creators
import logger from 'logger-module';
//
logger.log('somestuff');
Testing is still easily achievable by importing the logger and placing whatever spy/stub on its methods that you need to intercept.
From the Redux documentation:
/**
* Sends crash reports as state is updated and listeners are notified.
*/
const crashReporter = store => next => action => {
try {
return next(action)
} catch (err) {
console.error('Caught an exception!', err)
Raven.captureException(err, {
extra: {
action,
state: store.getState()
}
})
throw err
}
}
Raven being a third-party library.
If the library has its own state then it shouldn't be an issue using it in middleware (the state belongs in the library and not your app). If you're creating a state for it, for some reason, then that state should belong in the Redux store, probably under store.logger or something.
I just started to learn nodejs-postgres and found the pg-promise package.
I read the docs and examples but I don't understand where should I put the initialization code? I using Express and I have many routes.
I have to put whole initialization (including pg-monitor init) to every single file where I would like to query the db or I need to include and initalize/configure them only in the server.js?
If I initialized them only in the server.js what should I include other files where I need a db query?
In other words. Its not clear to me if pg-promise and pg-monitor configuration/initalization was a global or a local action?
It's also unclear if I need to create a db variable and end pgp for every single query?
var db = pgp(connection);
db.query(...).then(...).catch(...).finally(**pgp.end**);
You need to initialize the database connection only once. If it is to be shared between modules, then put it into its own module file, like this:
const initOptions = {
// initialization options;
};
const pgp = require('pg-promise')(initOptions);
const cn = 'postgres://username:password#host:port/database';
const db = pgp(cn);
module.exports = {
pgp, db
};
See supported Initialization Options.
UPDATE-1
And if you try creating more than one database object with the same connection details, the library will output a warning into the console:
WARNING: Creating a duplicate database object for the same connection.
at Object.<anonymous> (D:\NodeJS\tests\test2.js:14:6)
This points out that your database usage pattern is bad, i.e. you should share the database object, as shown above, not re-create it all over again. And since version 6.x it became critical, with each database object maintaining its own connection pool, so duplicating those will additionally result in poor connection usage.
Also, it is not necessary to export pgp - initialized library instance. Instead, you can just do:
module.exports = db;
And if in some module you need to use the library's root, you can access it via property $config:
const db = require('../db'); // your db module
const pgp = db.$config.pgp; // the library's root after initialization
UPDATE-2
Some developers have been reporting (issue #175) that certain frameworks, like NextJS manage to load modules in such a way that breaks the singleton pattern, which results in the database module loaded more than once, and produce the duplicate database warning, even though from NodeJS point of view it should just work.
Below is a work-around for such integration issues, by forcing the singleton into the global scope, using Symbol. Let's create a reusable helper for creating singletons...
// generic singleton creator:
export function createSingleton<T>(name: string, create: () => T): T {
const s = Symbol.for(name);
let scope = (global as any)[s];
if (!scope) {
scope = {...create()};
(global as any)[s] = scope;
}
return scope;
}
Using the helper above, you can modify your TypeScript database file into this:
import * as pgLib from 'pg-promise';
const pgp = pgLib(/* initialization options */);
interface IDatabaseScope {
db: pgLib.IDatabase<any>;
pgp: pgLib.IMain;
}
export function getDB(): IDatabaseScope {
return createSingleton<IDatabaseScope>('my-app-db-space', () => {
return {
db: pgp('my-connect-string'),
pgp
};
});
}
Then, in the beginning of any file that uses the database you can do this:
import {getDB} from './db';
const {db, pgp} = getDB();
This will ensure a persistent singleton pattern.
A "connection" in pgp is actually an auto-managed pool of multiple connections. Each time you make a request, a connection will be grabbed from the pool, opened up, used, then closed and returned to the pool. That's a big part of why vitaly-t makes such a big deal about only creating one instance of pgp for your whole app. The only reason to end your connection is if you are definitely done using the database, i.e. you are gracefully shutting down your app.