Hero background image
image (2)

React / Redux client with a MongoDB / Node / Express server

Project image

I worked on a project last year for Reuters that was an application, which educated journalists on identifying manipulated media. You can learn more about it here. This app was translated into 16 languages so it required global state in order to keep track of the language you were reading in at all times. While this was a super fun project to work on I still didn't quite grasp every nuance of working on a complex production-ready Redux app. So, in the spirit of a better and more productive 2021, I decided to test my luck at building the proverbial todo app and freshen up on some of these concepts.

State management

When it comes to building React applications, using a third-party tool such as redux to manage state can be a tad overkill, and for the most part, you could probably make do with the context API, which comes shipped with React 16. Having said that... I'm going to use Redux purely from an exploratory perspective. Instead of ranting on about how redux works (you can check out their excellent documentation here) I'm just going to point out a couple of places that were significant for me at least or areas that were just very cool. 🤓

Asynchronous

export const removeTodo = (user_id, todo_id) => {
  return (dispatch) => {
    return apiCall("delete", `/api/users/${user_id}/todos/${todo_id}`)
      .then(() => dispatch(remove(todo_id)))
      .then(history.push('/'))
      .catch((err) => {
        addError(err.message);
      });
  };
};

Since Redux isn't inherently asynchronous we can leverage middleware (in my case redux-thunk) to create asynchronous actions.

Modularity

While the learning curve for Redux at first can seem a little overwhelming and the set up a little tedious, you only need to do this once. When you understand the underlining principles there really isn't all that much more to get and you will be able to scale your applications much more efficiently and with greater modularity.

import { REMOVE_TODO, LOAD_TODOS, UPDATE_TODO } from '../actionTypes'

const todo = (state = [], action) => {
  switch (action.type) {
    case LOAD_TODOS:
      return [...action.todos];
    case REMOVE_TODO:
      return state.filter(todo => todo._id !== action.id);
    case UPDATE_TODO:
      return state.map(t => {
        if (t._id === action.id) {
          t.done = true;
        };
        return t;
      })

    default:
      return state;
  }
}


export default todo;

MongoDB

MongoDB is pretty awesome... For the longest time, any full-stack application I've been working on has used PostgreSQL. One of the things I enjoyed about MongoDB was its handy inbuilt methods for interacting with the DB.

exports.updateTodo = async function (req, res, next) {
  try {
    let updatedTodo = await db.Todo.findById(req.params.todo_id);
    await db.Todo.update(
      { _id: updatedTodo },
      {
        $set: {
          done: true,
        }
      }
   );
    return res.status(200).json(updatedTodo);
  } catch (err) {
    return next(err);
  }
};

I also really like the direction they're going with having to initialize a cluster in order to deploy the server on the backend. While I still really need to look more into the concept of clusters and sharding (way more complicated than necessary for this app) it's still awesome that MongoDB is streamlining this process and basically making it impossible (as far as I know) to deploy a server on Heroku without first setting up a cluster on Atlas.

The distinction between containers and components

I used to think everything was a component, but I like that with a Redux application you need to deliberately architect the places you want the state to reside with containers, which look a lot like components but have a few extra attributes. Anywhere you want to bring in your state you need to make sure to create your mapStateToProps function and use react-redux connect to hydrate your state.

function mapStateToProps(reduxState) {
  return {
    currentUser: reduxState.currentUser,
    errors: reduxState.errors,
  };
}

export default withRouter(
  connect(mapStateToProps, {
    authUser,
    removeError,
  })(App)
);

Authentication

I have spent so much of this year as a frontend engineer so it was really nice to re-visit auth and I mean that genuinely and sincerely. I like salt rounds and JWT and all that fun stuff even if it can be a pain to debug and make you feel like you're having an existential crisis.

exports.signin = async function (req, res, next) {
  try {
    let user = await db.User.findOne({
      email: req.body.email
    })
    let { id, username, profileImageUrl } = user
    let isMatch = await user.comparePassword(req.body.password)
    if (isMatch) {
      let token = jwt.sign(
        {
          id,
          username,
          profileImageUrl
        },
        process.env.SECRET_KEY
      )
      return res.status(200).json({
        id,
        username,
        profileImageUrl,
        token
      })
    } else {
      return next({
        status: 400,
        message: "That is an invalid user name or password mate"
      })
    }
  } catch (e) {
    return next({status: 400, message: "Error in catch of server / handlers - Invalid email or username"})
  }
}

Here we are signing up a user in the user handler with the help of JWT.

if (localStorage.jwtToken) {
  setAuthorizationToken(localStorage.jwtToken);
  try {
    store.dispatch(setCurrentUser(jwtDecode(localStorage.jwtToken)))
  } catch (e) {
    store.dispatch(setCurrentUser({}))
  }
}

Then in the index.js of the client using it again to decode the user token that was stored in local storage.

Conclusion

In my experience, I rarely get the opportunity to crank out a production-ready full-stack application by myself in a matter of days. Usually, I am hyper-focusing on a specific problem or really getting granular on some UX UI front-end development that needs to be primo. Now, this todo app is far from anything to write home about, but It's a great exercise to go through the motions of building a full-stack application from start to deployment, even if it's just a small silly app like this one. I implore you to try and do this from time to time whether you're primarily a frontend or backend dev. It's a great exercise. We all need to know what we're building on top of even if it's just at a higher level.

- You can check out this app here or go straight to the repo here.

alt

alt

alt

alt

alt

alt

alt

alt

site logo