How to access underlying express app in Keystone 6? - javascript

I'm using Keystone 6 for my backend and I'm trying to integrate Stripe, which requires access to the underlying express app to pass a client secret to the client from the server. This is highlighted in the Stripe documentation here: https://stripe.com/docs/payments/payment-intents#passing-to-client
I'm having trouble figuring out how to access the express app in Keystone 6 though and they don't seem to mention anything about this in the documentation. Any help would be appreciated.

The short answer is Keystone 6 doesn't support this yet.
The longer answer has two parts:
This functionality is coming
We've been discussing this requirement internally and the priority of it has been raised. We're updating the public roadmap to reflect this next week.
The functionality itself should arrive soon after. (Unfortunately I can't commit to a release date.)
Getting access to the Express app is possible, it's just a real pain right now
If you look at Keystone's start command you can see where it
calls createExpressServer().
This just returns an express app with the GraphQL API and a few other bits and bobs.
But there's actually nothing forcing you to use the build in keystone start command – You can copy this code, hack it up and just run it directly yourself.
Eg. you could replace this...
const server = await createExpressServer(
config,
graphQLSchema,
keystone.createContext,
false,
getAdminPath(cwd)
);
With...
const server = express();
server.get('/hello-world', (req, res) => {
res.send('Hello');
});
const keystoneServer = await createExpressServer(
config,
graphQLSchema,
keystone.createContext,
false,
getAdminPath(cwd)
);
server.use(keystoneServer);
And your /hello-world endpoint should take precedence over the stuff Keystone adds.
Unfortunately, this doesn't work for the dev command so, in your local environment you'll need to do it differently.
One option is to start a second express server that you control and put it on a different port and include your custom routes there.
You can still do this from within your Keystone app codebase but having different URLs in different environments can be annoying.
You'll probably need an environment variable just for your custom endpoints URL, with values like this in production:
# Production
GRAPHQL_ENDPOINT="https://api.example.com/api/graphql"
CUSTOM_ENDPOINT="https://api.example.com/hello-world"
And this in dev:
# Dev
GRAPHQL_ENDPOINT="http://localhost:3000/api/graphql"
CUSTOM_ENDPOINT="http://localhost:3100/hello-world"
It's ugly but it does work.
I'll update this answer when the "official" functionality lands.

Related

How can I reference data from MariaDB on web browser JS application?

I am trying to create a webpage that uses data from a MariaDB. My current idea (which has been giving me a lot of trouble) is to just connect to the database from the app.js file, which is the main script for my index.html.
const dotenv = require("dotenv");
dotenv.config();
const mariadb = require("mariadb");
const pool = mariadb.createPool({
database: process.env.DATABASE,
host: process.env.HOST,
user: process.env.USER_TOKEN,
password: process.env.PASSWORD,
});
// the rest of the code involves selecting from the db, and parsing the data
However, I have been running into many issues. I'm not too knowledgeable on all this, but I found that I need to webpack the file if I want to be able to use the "require" keyword. But I could not figure that out as I kept running into weird issues when using Browserify; I think there may be an incompatibility with MariaDB. I also looked into using JS modules, but I am not sure if that is possible with MariaDB.
I am trying to come up with another solution, potentially using some sort of API to a back end, which would make the GET request to the database, but I feel like it should not have to be that complicated for my sake (I also wouldn't really know where to start with this). All I basically want to do, is make a GET request to a MariaDB when the page loads on the client's browser and display that data on the webpage. Is there a simple way to do this?
I suggest you use nodejs to connect and query database as it will greatly resolve a lot of overhead for you..
The easiest way i can think of is using a prisma starter template here
https://github.com/prisma/prisma-examples/tree/latest/javascript/script
It also gives the added advantage of the ORM function...
Hope it helps.

How to secure API Keys in Environment Variables in a Vue CLI 4 and Electron project

I'm trying to develop a desktop app which would need to make a few private API calls, authenticated using some secret keys.
The keys are created for me by external IT service providers outside of my organisation - they are responsible for the security so there are a few constraints:
They said even though they have already taken steps on their end to secure the API and there are mitigation strategies in place even if a breach happens, but still they would like to make sure that I treat the keys with a security-conscious mindset and take whatever steps possible on my end as well to make sure they remain secured.
I'm not allowed to just create random middleware / gateway on a private server or serverless platform to perform the API calls on my app's behalf as these calls may contain business data.
I have done some research and from what I can find, the general recommendation is to set up a ".env" file in the project folder and use environment variables in that file to store the API keys.
But upon reading the Vue CLI documentation I found the following:
WARNING
Do not store any secrets (such as private API keys) in your app!
Environment variables are embedded into the build, meaning anyone can
view them by inspecting your app's files.
So, given the constraints, is there a way to store these keys securely in a Vue CLI 4 + Electron Desktop app project?
Thanks.
In general, especially if you have a lot of environment variables, it would be better practice to store environment variables in a dot env file (.env), however, it's possible that this file could be leaked when you package your electron app. So, in this case it would be better to store your environment variables from the terminal/command line. To do so follow this guide (https://www.electronjs.org/docs/api/environment-variables).
Keep in mind anything that requires the API key/private information try to keep it on the backend, i.e., the electron process and send the results to the Vue front end.
Here's an example of how you could implement this:
On windows from CMD:
set SOME_SECRET="a cool secret"
On POSIX:
$ export SOME_SECRET="a cool secret"
Main process:
// Other electron logic
const { ipcMain } = require("electron");
// Listen for an event sent from the client to do something with the secret
ipcMain.on("doSomethingOnTheBackend", (event, data) => {
API.post("https://example.com/some/api/endpoint", {token: process.env.SOME_SECRET, data});
});
Client side:
const { ipcRenderer } = require("electron");
ipcRenderer.send("doSomethingOnTheBackend", {username: "test", password: "some password"});
Also note, to use the ipcRenderer on the client side nodeIntegration needs to be enabled.
Here are some more resources to help you get started:
https://www.electronjs.org/docs/api/ipc-renderer
https://www.electronjs.org/docs/api/ipc-main

Is it possible to change the Express Session to a different store after creation

I am using memcached as a backing store for an ExpressJS session - chosen via an app configuration setting. I would like to fall back from memcached to memory if the memcached host cannot be contacted. (This isn't necessarily a production strategy as memcached is very reliable - it's more for when I forget to boot the Docker instance in dev, but could still be a production fail-safe.)
I first thought I could "app.use" a new session instance, and try to remove the first one, but I have read that it's difficult (if possible) to "un-use" Express middleware i.e. to swap the middleware in-place in the chain, or generally tinker with the chain once it's been setup.
The problem there is after the connection timeout period, the app middleware has been setup with many further services installed after the session middleware.
My second thought was can I re-configure the Express Session instance itself and change the store after it's been created? I could not see any way in the documentation.
My third idea was to wrap the express-session in a new "swappable store" class, but I'm wary of the scope of wrapping the entire interface.
For example in my app setup:
app.use(services.session.middleware());
// then a lot of other middleware...
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: false}));
app.use(cookieParser());
app.use(express.static(path.join(rootPath, 'public')));
...etc
And in the session service, which selects and configures the session instance:
function middleware() {
// default / Memory Store options
const opts = {
resave: false,
saveUninitialized: false,
...etc
}
};
// Install any configured backing store
if(config.session.storage === "memcached"){
const MemcachedStore = require('connect-memcached')(session);
opts.proxy = 'true';
opts.store = new MemcachedStore({
hosts: ...,
secret: ...
});
const errorHandler = (type, details)=> {
/* HERE I WOULD LIKE TO RE-MAKE THE SESSION USING MEMORY
* AND DISCARD THE MEMCACHED ONE
* (THIS HAPPENS AFTER APP BOOT HAS FINISHED)
*/
console.error( String.format("Memcached {3} with host:{0} details:{1}.", details.server, details.messages.join( '' ), type));
}
opts.store.client.on('failure', details => errorHandler('failure', details));
opts.store.client.on('issue', details => errorHandler('issue', details));
}
return session(opts);
}
I think there are several approaches you can try for this. The first is to configure a storage system based on which configuration you have supplied on startup (probably via a module like config or nconf). The second is to run a quick check when booting up the app to make sure it can access the memcache service, and if it can't then fallback to memory with an error.
I would be fairly weary of doing either of these, since you're using docker and it should be easy to boot memcache. This is because you'll be introducing code which might trigger in production should there be some connection issue, and then you might find yourself accidentally serving sessions out of memory rather than something like memcache potentially without realising.
I'll expand on both strategies here and provide a third possibly better option.
1. Choose the cache system based on a config
This should be fairly straight forward, simply extract your configuration into some sort of config manager / environment variables (checkout config or nconf). When starting the application and connecting your session middleware, you can pull out all the possibly configurations, see which exist and attach one based on that. This is similar to how your if (config.session.storage === 'memcache") looks at the moment. Just use a fallback of not configuring one and the express-session middleware will fall back to memory. This way you can leave out the configuration completely and just always use memory for development.
2. Run a test before connecting to the desired service
In combination with the above, if memcache details are provided you could run a quick test by attempting to store something in memcache on startup. Perhaps new Date(); to signal when the application booted up? If this throws an error, then just don't attach the MemcachedStore to the express-session options and you can safely destroy the MemcachedStore.
3. Throw an error if you cannot connect to Memcached
This is in further combination to #2. If you identify that memcache configurations are provided, then I would personally do a check to see if you can contact the serivce and if not then throw an error and stop the application. This would mean that in development you immedietely know the issue, and in production you would as well and can trigger automatic alerts for yourself based on the fact that the application failed to start.
This is probably the best and most robust solution, generally doing a silent fallback is not a great idea when talking about connected services as things can go wrong and you have no idea. I appreciate that this is for development purposes and you need to be pragmatic, but if it saves you accidentally serving all sessions from your servers memory then this would be super beneficial.

Make hapi plugin available in modules

I'm refactoring my Hapi server to use reusable modules instead of performing logic in my route handlers. I have a plugin registered in my Hapi server for MongoDB connection pooling, which I'd like to be able to access in these modules. Is there a way to export the server object itself, or do I need to rewrite my modules to accept the request object as an argument? I'm using node 0.12.12 and Hapi 8.4.0.
I already tried module.exports = server; in the file where my server is defined, and then requiring the server object from a different file, (both with var server = require('../index.js').server; and var server = require('../index.js')(server);, but I either get an error or undefined.
The closest thing I could find to an answer was this issue from a few years ago, on an older version of Hapi: https://github.com/hapijs/hapi/issues/1260
- but it looks like this was never really resolved.
Well, I'm an idiot, but maybe this will help somebody else out:
It seems module.exports cannot be called within a callback, according to the node documentation. So I moved this statement to the bottom of my index.js:
module.exports.server = server
And then in my other modules, called:
var server = require('../index.js');
And was able to access the plugins contents as server.server.plugins
HTH

In Node.js, is it normal to create several "server" objects, but only bind one to a port?

I'm just about done reading "Node.js in Action", and I'm trying to put together the pieces of Node.js --> Connect --> Express. I have a question about the "servers" that we create in Node.
node_server = http.createServer();
connect_app = Connect();
express_app = Express();
In the code above, is it true that connect_app is basically a "subclass" of node_server? (I know, this is JavaScript, so we don't really have subclassing, but I don't know what else to call it; extension?). And likewise express_app is basically a "subclass" of connect_app? It's my understanding that all of these objects are servers which could be bound to a port and respond to requests, but that in practice we typically only bind ONE of them to a port and use it to proxy requests to other server objects.
Am I on the right track in learning this?
First of all, shake off the idea that there are 3 running servers - because there's only one.
Express is a framework that relies on Connect, which is another framework/set of middlewares. Further, Connect relies on the NodeJS's API (HTTP module). Basically an abstraction, one on top of another.
An analogy is that Express is a car, Connect is like an engine, NodeJS is the engine parts. You only have one running car (one server in your case), but multiple components powering it.
#josh3736 Has commented a better explanation how it works.

Categories

Resources