Node.js server restart drops the sessions - session

I'm having fully functional user signup/authentication system using express and connect middleware.
app.use(express.session({store: require('connect').session.MemoryStore( {reapInterval: 60000 * 10} ) }))
The only problem is that sessions drop every time you perform server restart.
https://github.com/remy/nodemon - and nodemon restarts node.js every time it detects a file change.
How can I have persistent sessions ?

Like your code is telling you are using MemoryStore. This is volatile and gets cleared on restart. I would advise you to use connect_redis to persist your session. Redis is an extremely fast store.
Download redis
compile redis: make
Start server: ./redis-server
npm install connect-redis
var connect = require('connect') , RedisStore = require('connect-redis');
connect.createServer(
connect.cookieParser(),
// 5 minutes
connect.session({ store: new RedisStore })
);
This is just to get you started quickly. You should read the documentation and configure redis if you want to get most out of redis.

I was trying to get Redis on track using express.js, Google sent me here. The express implementation changed:
var express = require('express'),
RedisStore = require('connect-redis')(express);
Another important thing is the order of express configurations.
app.configure(function(){
app.enable('strict routing'); // removes trailing slash
app.set('views', __dirname + '/views');
app.set('view engine', 'jqtpl');
app.register('.html', require('jqtpl').express);
app.use(express.favicon());
app.use(express.methodOverride());
app.use(express.compiler({src: __dirname + '/public', enable: ['sass']}));
app.use(express.static(__dirname + '/public'));
app.use(app.router);
app.use(express.bodyParser());
app.use(express.cookieParser());
app.use(express.session({secret: _.config.secret, store: new RedisStore}));
});
cookieParser & session configurations need to be at the end of the configurations, and cookieParser must be placed right before express.session.
Hope that helps, I ran in both of these problems.

I agree with everybody about Redis, but I think that different technologies are a problem in terms of software maintenance. If you are using MongoDB for example there is connect-mongo (https://npmjs.org/package/connect-mongo), if you are using MySQL there is connect-MySQL (https://npmjs.org/package/connect-mysql), connect-couchdb for CouchDB (https://npmjs.org/package/connect-couchdb) and so on.

also, if you're using express, you need to provide a secret when telling the app to use the redis middleware.
so, follow Alfred's recipe above, but do the following...
var express = require( 'express' );
var RedisStore = require('connect-redis');
app.use( express.cookieParser() );
app.use( express.session( { secret: "keyboard cat", store: new RedisStore }));

When node dies I would imagine the memory store you're using dies.
Persist the sessions to disk?

Related

How to connect nextjs app to graphql studio

I'm trying to figure out how to open the apollo studio for my next js app.
When I run the local host, I get a message that says:
Server started at http://localhost:5555/graphql
When I click that link, I get a page that says:
GET query missing.
I'm trying to find a way to get to the apollo studio explorer.
For others looking (or maybe for myself the next time I forget), the address: http://localhost:5555/graphql gets inserted in the sandbox address bar, that you find at the url: https://studio.apollographql.com/sandbox/explorer. It won't work if you put the local host address in the url bar
I faced the same issue and have managed to solve it by connecting to apollo studio as a deployed graph (not using the sandbox) but running locally.
Firstly I followed this tutorial https://master--apollo-docs-index.netlify.app/docs/tutorial/production/ which does not use NextJS but it does connect a react app to the apollo studio sandbox then by section 5 it connects the deployed graph to apollo studio. Unfortunately section 5 is quite outdated so i will try to fill in the blanks and get you up and running.
After you have set up an account in apollo studio add a new graph (+ New Graph button). Use whatever architecture you like but I tried this using 'supergraph'.
On the next page ('Publish your schema using Federation') I used the 'schema document' tab and pipeline: 'Federation 2 Supergraph'.This generates the 2 of the 3 env keys you need to add to your local env file and feed it into your app. keys as follows:
APOLLO_KEY - this starts 'service:' and ends before the space, it is a single line about 50 characters long.
APOLLO_GRAPH_REF - this can be found at the end of the line below the APOLLO_KEY. it is a single word with a '#' symbol in the middle.
APOLLO_SCHEMA_REPORTING=true - written as shown here.
Do not close the 'Publish your schema using Federation' page/ re-open it if you have closed it as it will indicate that you have successful connected the graph when you run the app locally after the next step.
Start the app locally using the CLI and in the browser request a page that queries the apollo server.
Watch the CLI as the page is served and you should see the comment 'Apollo usage reporting starting!', also the 'Publish your schema using Federation' page should confirm the graph has been connected. Now you can use all the features of the sandbox as well as monitoring etc.
Hope this helps.
The reason why Next.js doesn't allow you to connect to Apollo Studio is because Next.js does not allow CORS by default in api handlers.
Apollo Studio tries to send a request from its own domain and it's blocked by Next.js default setup.
Let's assume you have your graphql/Apollo server in your NextJs app at /api/graphql path. When you navigate to that path (from your local) by using http://localhost:3000/api/graphql it will show you the welcome page and allow you to access Apollo Sandbox.
Once you enter the Apollo Sandbox in the bottom right corner it will display this message:
When you run the diagnose problem on your local you'll see the following message:
$ npx diagnose-endpoint#1.1.0 --endpoint=http://localhost:3000/api/graphql
Diagnosing http://localhost:3000/api/graphql
⚠️ OPTIONS response is missing header 'access-control-allow-methods: POST'
⚠️ POST response missing 'access-control-allow-origin' header.
If using cookie-based authentication, the following headers are required from your endpoint:
access-control-allow-origin: https://studio.apollographql.com
access-control-allow-credentials: true
Otherwise, a wildcard value would work:
access-control-allow-origin: *
(📫 Interested in previewing a local tunnel to bypass CORS requirements? Please let us know at https://docs.google.com/forms/d/e/1FAIpQLScUCi3PdMerraiy6GpD-QiC_9KEKVHr4oDL5Vef5fIvzqqQWg/viewform )
The solution for the problem looks like this:
import type { NextApiRequest, NextApiResponse } from "next";
import Cors from "cors";
import { server } from "../../apollo";
// Initializing the cors middleware
// You can read here: https://github.com/expressjs/cors#configuration-options
const cors = Cors({
methods: ["POST"],
});
// Helper method to wait for a middleware to execute before continuing
// And to throw an error when an error happens in a middleware
function runMiddleware(
req: NextApiRequest,
res: NextApiResponse,
fn: Function
) {
return new Promise((resolve, reject) => {
fn(req, res, (result: any) => {
if (result instanceof Error) {
return reject(result);
}
return resolve(result);
});
});
}
export const config = {
api: {
bodyParser: false,
},
};
const startServer = server.start();
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
// Run cors middleware (to allow Apollo Studio access)
await runMiddleware(req, res, cors);
// run apollo server
await startServer;
await server.createHandler({ path: "/api/graphql" })(req, res);
}
It combines using the Apollo server and this CORS example
the import:
import { server } from "../../apollo"
from the example above is the apollo server that looks like this:
import { ApolloServer } from "apollo-server-micro";
import { typeDefs } from "./schema";
import { resolvers } from "./resolvers";
export const server = new ApolloServer({
typeDefs,
resolvers,
});
You can also use alternative options like embedding sandbox into your app but I'm finding the above solution a bit easier for my current needs so hope it helps you as well.

GraphQL subscription from multiple browsers/tabs

I have an react frontend and a python backend (using ariadne==0.13.0, uvicorn==0.15.0, uvicorn[standard]==0.15.0, fastapi==0.68.1) communicationg over graphql subscriptions. Everything works fine as long as I do not reload the page or load the page in a new browser window from same IP. Then the page crashes and takes some time to recover - Depending on the websocket timeout configured in uvicorn. I am experiencing the same issue with both my frontend and the graphql playgorund.
I understand that the different browsers or tabs are identified with the same IP, Port and protocol what possibly messes up the existing connection, but still it should be possible the use the page from different tabs as seen in:
https://fastapi.tiangolo.com/advanced/websockets/
My code:
SCHEMA = load_schema_from_path("schema.graphql")
query = QueryType()
subscription = SubscriptionType()
app = FastAPI()
schema = make_executable_schema(SCHEMA, [query, subscription])
graphql_server = GraphQL(schema, debug=True)
app.add_route("/graphql", graphql_server)
app.add_websocket_route("/graphql", graphql_server)
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_methods=["POST", "GET"],
allow_headers=[
"accept",
"accept-language",
"content-language",
"content-type",
"x-apollo-tracing",
],
)
app.debug = True
uvicorn.run(app, host='0.0.0.0', port=7996)
The default setup for uvicorn is single threaded and the method implementing the subscription was synchronous and blocking. I had to reimplement it in an async manner.

Is there any way to make Skipper work with socket.io?

My problem is as simple as annoying. I'm developing a Sailsjs app, and I would just like to use socket.io to upload a file.
I usually make use of Skipper which is the recommended Sails' upload handler, but the req.file() object stay undefined (though it work well with http requests).
Skipper is not capable of that. At least I cannot find any proof in the documentation: https://github.com/balderdashy/skipper
Since sails#0.11.0 there is support for socket.io v1.2.1 which has support for binary data transfer:
http://socket.io/blog/introducing-socket-io-1-0/#binary-support
You want to transfer data from client to server. However most example you find is the other way round, e.g. https://stackoverflow.com/a/24124966/401025:
Server sends image to client:
require('socket.io')(3000).on('connection', function(socket){
require('fs').readFile('image.png', function(err, buf){
socket.emit('image', { image: true, buffer: buf });
});
});
Client receives image:
socket.on("image", function(image, buffer) {
if(image){
// do something with image
}
});
I have not tested if it works from client to server. You have to try ;)

apiKey key ID and secret is required even though they're there in express-stormpath

I'm trying to use express-stormpath on my Heroku app. I'm following the docs here, and my code is super simple:
var express = require('express');
var app = express();
var stormpath = require('express-stormpath');
app.use(stormpath.init(app, {
website: true
}));
app.on('stormpath.ready', function() {
app.listen(3000);
});
I've already looked at this question and followed the Heroku devcenter docs. The docs say that for an Heroku app, it's not necessary to pass in options, but I've still tried passing in options and nothing works. For example, I've tried this:
app.use(stormpath.init(app, {
// client: {
// file: './xxx.properties'
// },
client: {
apiKey: {
file: './xxx.properties',
id: process.env.STORMPATH_API_KEY_ID || 'xxx',
secret: process.env.STORMPATH_API_KEY_SECRET || 'xxx'
}
},
application: {
href: 'https://api.stormpath.com/v1/applications/blah'
},
}));
To try and see what's going on, I added a console.log line to the stormpath-config strategy valdiator to print the client object, and it gives me this:
{ file: './apiKey-xxx.properties',
id: 'xxx',
secret: 'xxx' }
{ file: null, id: null, secret: null }
Error: API key ID and secret is required.
Why is it getting called twice, and the second time around, why does the client object have null values for the file, id and secret?
When I run heroku config | grep STORMPATH, I get
STORMPATH_API_KEY_ID: xxxx
STORMPATH_API_KEY_SECRET: xxxx
STORMPATH_URL: https://api.stormpath.com/v1/applications/[myappurl]
I'm the original author of the express-stormpath library, and also wrote the Heroku documentation for Stormpath.
This is 100% my fault, and is a documentation / configuration bug on Stormpath's side of things.
Back in the day, all of our libraries looked for several environment variables by default:
STORMPATH_URL (your Application URL)
STORMPATH_API_KEY_ID
STORMPATH_API_KEY_SECRET
However, a while ago, we started upgrading our libraries, and realized that we wanted to go with a more standard approach across all of our supported languages / frameworks / etc. In order to make things more explicit, we essentially renamed the variables we look for by default, to:
STORMPATH_APPLICATION_HREF
STORMPATH_CLIENT_APIKEY_ID
STORMPATH_CLIENT_APIKEY_SECRET
Unfortunately, we did not yet update our Heroku integration or documentation to reflect these changes, which is why you just ran into this nasty issue.
I just submitted a ticket to our Engineering team to fix the names of the variables that our Heroku addon provisions by default to include our new ones, and I'm going to be updating our Heroku documentation later this afternoon to fix this for anyone else in the future.
I'm sincerely sorry about all the confusion / frustration. Sometimes these things slip through the cracks, and experiences like this make me realize we need better testing in place to catch this stuff earlier.
I'll be working on some changes internally to make sure we have a better process around rolling out updates like this one.
If you want a free Stormpath t-shirt, hit me up and I'll get one shipped out to you as a small way to say 'thanks' for putting up with the annoyance: randall#stormpath.com
After endless hours, I managed to finally get it working by removing the add-on entirely and re-installing it via the Heroku CLI and then exporting variables STORMPATH_CLIENT_APIKEY_ID and STORMPATH_CLIENT_APIKEY_SECRET. For some reason, installing it via the Heroku Dashboard causes express-stormpath to not find the apiKey and secret fields (even if you export variables).

why is my express middleware session undefined?

I'm trying to use the express middleware for sessions (I'm not understanding but I feel that I am very close).
the reason I have ended up asking is that the express docs (http://expressjs.com/api.html#middleware) are calling it express.cookieSession where as everyone else (mostly on here) Ive seen discussing it have been calling it express.session. I'm really not sure now, I just have a big lump of possibly useful code ??? but every example I see is different ... how does it work?
var express = require('express')
, http = require('http');
, app = express();
store = new express.session.MemoryStore;
app.use(express.cookieParser());
//app.use(express.cookieSession());
app.use(express.session({secret:'whateverman',key:'express.sid',store:store}));
app.use(app.router);
app.all('/*',function(req,res,next){res.header("Access-Control-Allow-Origin","*");res.header("Access-Control-Allow-Headers","X-Requested-With");next();});
var server = http.createServer(app);
server.listen(8000);
var io = require('socket.io').listen(server);
io.sockets.on('connection',function(socket){
socket.on('reglogin',function(_){_.session.e='some#email.com';});
});
socket.on('reglogin' produces:
TypeError: Cannot set property 'e' of undefined
//-------------------------------update on question
if express is dependant on connect but express can access connects middleware as if it were its own, eg:
app.use(express.cookieSession()); //app is express
app.use(connect.cookieSession()); //does exactly the same
then surely the same logic would apply to socket.io which is dependant on express:
io.use(connect.CookieSession()); //io is socket.io
Am I wrong here? does Socket.io have the same .use method? update:(answer is no to io.use)
io.interoperate(app.use(express.cookieSession())); LOL
----------------------------UPDATE---------------------------------
I've followed the following npm modules guide lines in a despirate attemp to get sessions working and filed each and every time on handshake with no cookie:
express.io
session.socket.io
session.io + sessionstore
currently using the latter with console log:
warn - handshake error Could not find cookie with key: connect.sid

Resources