How to maintain sessions with React frontend and Rails backend
While I was working on my final project for Flatiron School I’ve encountered a few problems with sessions and I wanted to cover this subject in a little more depth. I gonna give you a heads-up and say that there is no right answer that suits all of the cases, and that’s why I wanna go over them and talk about the options.
First of all, this publication is targeted at people who’s want to deploy their projects and I’m going to connect the dots on a few deployment options and how can you handle your sessions in those cases.
Let’s talk about app deployment for a second. As a student of Flatiron school I’ve built 4 deployable projects altogether, and most of them I deployed on Heroku, which offers you a free tier option and it’s perfect for showcasing your projects. The only downside of it that after 30 minutes of inactivity the server goes to sleep mode, and to wake it up takes about 10–20 seconds. The benefit of Heroku that is you can deploy Rails or React or else what to their hosting platform, very convenient.
Firebase, is the other option, however for free you can upload only the npm application, so React app will do just fine, but what about the backend? You can throw your backend on google cloud and deploy it that way, but it won’t be free anymore.
So the solution I found for myself is to deploy React front end to the Firebase and Rails backend to the Heroku. Seems like a bit more work to do but the benefit is pretty amazing in the sense that you get your front end of the application starting up immediately while the backend is loading. You can play around with this a many ways but at least you have an engagement with the user right away.
The other option for deployment is that you can deploy both on your project to Heroku at the same dyno by just adding build packs like that:
$ heroku buildpacks:add heroku/nodejs --index 1
$ heroku buildpacks:add heroku/ruby --index 2
And your app will load as a whole at the same time and will be server on the same domain.
So now let’s talk about maintaining the sessions with these two options of deployment:
- Deploy Backend and Frontend to Heroku on the same domain name.
- Deploy Backend to the Heroku and Frontend to the Firebase which would be hosted on two different domain names.
Case 1:
Rails are an amazing and very powerful framework. If you’ve built a sole rails application then you most likely know that rails provide you with sessions that are being encrypted and decrypted behind the scene and the whole experience of maintaining is made very seamless and smooth. Why don’t use them? It does require a few initial configurations but the rest is pretty much the same as if it would be in a straight-up rails application.
Let’s configure our backend API:
Let’s create a session_store.rb in config/initializers
Next, let’s add a few lines of code in config/application.rb to enable the sessions, since they aren’t available by default if you have an API flag turned on.
and add
include ActionController::Cookies
to the application controller
So that’s pretty much it. With this set up you can deploy front and back to Heroku and be able to utilize rails sessions, if this does not work for you, even though it should you can add a gem ‘rails_same_site_cookie’ in your Gemfile and run bundle install.
Case 2:
In case two we cannot utilize rails session control in our app but there is another solution: Local Storage.
Well there is also a Session Storage as well and to see the difference between a cookie, Session Storage and Local Storage I would recommend watching the video below but We’ll stick with Local Storage for now.
Let’s install Local Storage tools first:
$ npm install reactjs-localstorage
or
$ yarn add reactjs-localstorage
and Import it to the component where you planned to use it and a few examples on how to use it:
import {reactLocalStorage} from 'reactjs-localstorage';reactLocalStorage.set('var', true);reactLocalStorage.get('var', true);reactLocalStorage.setObject('var', {'test': 'test'});reactLocalStorage.getObject('var');reactLocalStorage.remove('var');reactLocalStorage.clear();
This is pretty much it, there are a few examples of how you might use it in functional or class components:
import React from "react";
import "./styles.css";
export default class App extends React.Component {
constructor() {
super();
this.state = JSON.parse(window.localStorage.getItem('state')) || {
count: 0
}
}
setState(state) {
window.localStorage.setItem('state', JSON.stringify(state));
super.setState(state);
}
increaseCount = () => {
return this.setState({...this.state, count: this.state.count + 1});
}
decreaseCount = () => {
return this.setState({...this.state, count: this.state.count - 1});
}
render() {
return (
<div className="App">
<h1> Count {this.state.count} </h1>
<button onClick={this.increaseCount}>+</button>
<button onClick={this.decreaseCount}>-</button>
</div>
);
}
}
Or Functional:
import React, { useEffect, useState } from "react";
import "./styles.css";
export default function App() {
const [count, setCount] = useState(0);
useEffect(() => {
setCount(JSON.parse(window.localStorage.getItem('count')));
}, []);
useEffect(() => {
window.localStorage.setItem('count', count);
}, [count]);
const increaseCount = () => {
return setCount(count + 1);
}
const decreaseCount = () => {
return setCount(count - 1)
}
return (
<div className="App">
<h1> Count {count} </h1>
<button onClick={increaseCount}>+</button>
<button onClick={decreaseCount}>-</button>
</div>
);
}
Thank you for reading and good luck!