Learn MERN and Get a Head Start At Your Next Hackathon

Brayden Cloud
Hello Asterisk.
Published in
11 min readJul 14, 2020
Throwback to HackDFW 2015, where hacker Henry Vo wishes he had learned MERN

Summary

If you’ve ever attended a hackathon, you’re familiar with the excitement that comes with learning new technologies and sitting in a room full of passionate developers. This is a great environment for testing your skills and learning new ones, but with only 24 hours to create something breathtaking, where do you start?

Don’t start at the beginning.

Not the very beginning at least. The most tedious part of starting a hackathon project is setting up your environment and connecting all the pieces together (frontend, backend, and database). To help hackers bypass this boring step, we at Asterisk have created an open-source template to allow you to hit the ground running come hacking hours. Feel free to switch from the main branch to the feature-complete branch if you want to avoid writing the code yourself.

The hackathon-starter template is designed to be a simple, yet thorough, starting point to help you maximize your development speed. It features a simple file structure to minimize the amount of “boilerplate” code you need to write and it is written entirely in TypeScript to minimize the number of languages your team needs to learn.

Bonus: TypeScript comes with the added benefits of being less error-prone than JavaScript and very well supported and documented in online communities.

First Things First

Before diving into the code, you must first make sure MongoDB is installed and set up. To install MongoDB Atlas, follow the Getting Started Guide on MongoDB’s website.

With MongoDB Atlas installed, you can now create a new database for your project by running the following commands.

$ mongo
$ use hackathon-starter

The mongo command connects you automatically to your local database. The use hackathon-starter command connects to the hackathon-starter database, or in this case, creates and connects to a new database. By default, your project will be looking for a database named “hackathon-starter”, but you are not required to use this name.

Set Up Your Project

Creating your project repository on Github is super easy with the use of templates. To get started, navigate to the hackathon-starter repository and click “Use this template.” From here you can name your repository and it will be copied to your Github automatically! Now you’re ready to clone your new repository.

$ git clone <my-repository-url>.git
$ cd <my-repository>

If you’re unfamiliar with the git CLI, check out the basic git section of the Git Handbook.

The last part of the setup is installing the dependencies. Hackathon-starter uses Yarn to manage its dependencies. In both the backend and frontend folders, you’ll find a package.json file which shows all of the external packages your project needs in order to work. For more information on Yarn and package.json take a look at the Yarn Documentation.

To install the dependencies defined in package.json, run the following command in both the frontend and backend folders:

$ yarn

This will automatically download all dependencies into a node_modules folder as well as create a yarn.lock file, which can safely be ignored.

Now that your database is set up, your project is downloaded and your dependencies are installed, you can finally launch your web application! Open a terminal in both the backend and frontend folders and run the following command in each.

$ yarn start
Hackathon Template Home Screen

After a moment, your application will launch and you should see this webpage. You’re now ready to start bringing your idea to life!

Bringing Your Idea to Life

For illustrating the process of adding features to this project, you will be creating a revolutionary product: another To-Do app. With this golden-ticket startup idea in mind, it’s time to explore the implementation of this feature, from backend to frontend.

The Backend

Before your to-do app is ready to serve the absent minds of the world, you must first design a way to create and store your tasks; this starts with the backend.

Open the backend/src folder to start exploring the file structure. You’ll notice the backend is broken up into three separate sections along with the entry point, index.ts. Starting with the entry point, you’ll be presented with the following code.

The sole responsibility of this file is to initialize the server (line 19). Take a look at the comments above to get an idea of what each part does. Currently, there is only one route in use: /todo, which uses todoRouter to handle all requests made on it.

The Routing Layer

What is todoRouter? Opening up todo.ts in the routes folder, you’ll find the answer. As mentioned before, todoRouter handles all requests on the /todo route. It does this by defining a handler for various “subroutes” (e.g. /todo/:id/uncomplete). Add the following POST handler into src/routes/todo.ts.

In this example, todoRouter is defining a function, called a request handler, that will be called when you make a request to the endpoint /todo/:id/uncomplete. Note that any part of the path preceded with a colon (e.g. /:id) denotes a variable. You’ll notice that this variable is retrieved on line 6.

Looking at the body of this function, you can identify two main steps: calling the “business logic” and returning a response to the user. Connecting to the database, performing calculations or making decisions is not the concern of the request handler. This is intentional as separating concerns in your application is a great way to keep your code scalable and easy to read. To perform the business logic, the routing layer depends on the service layer.

The Service Layer

In the services folder, open the file todo.ts. Following the example from above, add the uncompleteTodo function below in src/services/todo.ts; make sure to go back and import this function in routes/todo.ts. This function is the workhorse that performs the task of marking your to-do item as incomplete.

Although it’s a small example, this is where the magic happens. In the fifth line, the “todo” collection is referenced in the database and is immediately used to update a to-do item in the following line. If the update is not successful, this function will throw an error which will be caught by the routing layer and returned to the user in a friendly format.

With the help of the useCollection function from db.ts, this logic is able to stay short and concise. The db.ts file contains a set of functions which provide a convenient way to connect to the database and reference collections. For more information on the MongoDB npm package, check out the npm documentation.

The Frontend

With your new API endpoints up, you’re finally ready to make this feature available on the frontend. The frontend for this template is a typical React + Redux project. This means you will be creating your webpages using React, and you will be storing the data needed by those pages in Redux. This section will assume you have some familiarity with these two libraries, but you can check out the React and Redux documentation if you need a crash course!

Diving into the frontend folder, you’ll find four distinct sections: components, models, pages, and store. The components folder is where all components which are not webpages live (e.g. Navbar, Footer, Layout). The models folder stores only type definitions such as the interface for your TodoItem model. In the pages folder, you’ll find all components which represent a single page or route in the application. Lastly, the store folder contains the logic for the Redux state.

In the root of these directories, you’ll find the entry point of your app: App.tsx. Start with this file to get an understanding of how this app operates.

The first detail to take note of is the two components that wrap the entire component: Provider and Router. On a high level, the Provider component is what allows your app to access the global Redux state while the Router component allows your app to resolve urls and create routes. The Switch component is responsible for resolving the route in the browser and choosing a component to render.

How does the Switch know what to render? Notice that there is a Route component being rendered for each item that comes from routes.ts. Here, the definitions from routes.ts are used as props for the Route. For more information on how routing works in React, take a look at the react-router documentation.

Routes

For the purposes of a hackathon project, the basics of routing should be sufficient. Take a look at routes.ts to see how your to-do page is found and rendered.

In this example there are only two routes: the root route (“/”), and the todo route; the root route renders the HomePage component while the todo route renders the TodoPage component. This configuration is exported as a list of RouteProps which is then injected into individual Route components in App.tsx.

Pages

In the example above, you’ll notice that the todo route causes the TodoPage component to be rendered. This component is where you will create your UI. There’s a lot to digest in this file, so take it one step at a time!

This first section is where you will define all of the CSS (styles) that make your component look beautiful. Because this template comes with Material UI by default, this should be really easy!

In this snippet, React Hooks are used to create an instance of dispatch (for using Redux) and fetch the specific data we want from Redux.

The useEffect function is a React Hook which allows you to execute code when a component initializes or updates. In this case, fetchTodos will be dispatched when the component loads for the first time.

The addTodo and checkTodo functions exist to dispatch actions to Redux for you. Doing so will trigger the API call and cause the state to be updated! Exciting right? Updating state will be covered in-depth in the next section.

Lastly, the JSX is returned to render the component. In order to add the ability to uncomplete an item, you need a button to press. The “complete todos” section of the JSX is a great place to add this.

Notice that as it stands, this section returns a p tag for each to-do item in completeTodos. To add your button, make the following change.

Oh no it broke! Don’t fret, add the uncheckTodo function below checkTodo to fix the error.

It still doesn’t work! Although your component is complete, the uncompleteTodo function does not exist. This is where Redux comes into play.

Redux

As mentioned before, this project uses Redux to manage state throughout the application. Redux is a state management solution that records data as a sequence of actions. These actions are essentially records of events that occur in the app and are saved in a “database” known as the store; actions can be dispatched (sent to Redux) in order to update the state. Redux is typically implemented as a wrapper around App.tsx, allowing it to take effect in all child components.

The infrastructure for your state starts in store/index.ts.

To break down what’s going on here, first take a look at line 7. The Action type encompasses all types of actions that can be dispatched to Redux. As mentioned before, an action is essentially a record that is sent to Redux and allows the store to figure out how it should change the state of your app.

On line 9 is where the main reducer is created (more on reducers later). The combineReducers function takes an object full of reducer functions and combines them together into one (hence the name). In this example there is only one reducer (todo), but this file is set up to allow more reducers to be easily added.

On the very next line, line 10, the State type is defined. The purpose of this type is to provide a definition for the entire state of the application. This will make your text editor easier to work with as it will point out any type of error you make while accessing and updating the state.

In the last line, the Redux store is exported from index.ts, where it will be used by the Provider component in App.tsx. The responsibility of the store is to execute the reducer every time an action is dispatched and keep the global state up-to-date.

The call applyMiddleware(thunk) adds the Thunk middleware to Redux, allowing you to dispatch asynchronous actions (which are synchronous by default). You’ll need this in order to make API calls.

At this point, no real state-management logic has been added, but that changes when you look at store/todo.ts. Here lies the meat of your to-do state. This file can be broken up into five important parts, each of which will be represented below with an example.

The first part is the TodoState. This is an interface which defines what the state should look like for tracking to-do items. You’ll notice it consists mostly of variables for loading state and errors while the to-do items themselves are kept in the todos array.

Following TodoState, there exists another set of interfaces: the action types. Each action type defines the type and payload for an action which can be dispatched to Redux. In this example, when the “TODO_CREATED” action is dispatched, it is expected to come with a TodoItem in the payload. Add the following two action types below TodoCompleted.

The third, and most important, part of the to-do state is the reducer. The “reducer” sounds complicated, but is really just a function. It is a special function which takes a copy of the state as one argument, an action that is dispatched as the second argument, and returns a new state object. This is how the actions you dispatch update the state! Add the following cases to the reducer function to allow for uncompleting to-do items:

Notice that in each switch case, a new state object is returned; however, it is slightly mutated based on the type of action that is provided. If the “TODO_UNCOMPLETED_STARTED” action is provided, a new state is returned with isUncompletingTodo set to true. If “TODO_UNCOMPLETED” is provided, then the state is returned with the updated to-do item.

Directly below the reducer, you’ll find the action functions. Thanks to Thunk, these are dispatchable functions which in turn can dispatch their own actions to update the state. They also request your to-do items from the backend. Add the uncompleteTodo function below.

In the second line, you’ll see that an action is being dispatched immediately which will update the state to reflect the loading status of the API call. Once the API call returns, another action is dispatched based on the result. If the result is not successful, a “TODO_ERRORED” action is dispatched, otherwise the “TODO_UNCOMPLETED” action dispatches along with the updated to-do item.

The last section is the selectors. Like the reducers, selectors are just functions.

The difference is that they take the state object as an argument and return a very specific part of it rather than a brand new state. Think back to TodoPage.tsx where these selectors are used to get the complete and incomplete to-do items.

Test It Out

With the additions to Redux in place, you’re ready to fire it up! Launch the backend and frontend by running yarn start in each folder and take a look at the result. If all goes well, you should now be able to navigate to http://localhost:3000/todo on the frontend, add to-do items, mark them as complete and… mark them as incomplete! 🎉

To-do page functionality

Congratulations! With your life-changing feature complete, you’re ready to jump to light-speed at your next hackathon!

Your project is set up and ready to go, but you still have a long 24-hour journey ahead before you win that Nintendo Switch. Check out the “More Resources” section for more ways to take your hacks to the next level and learn more about the technologies used in this project.

More Resources

Written by Brayden Cloud, Software Engineer

Twitter | Facebook | Instagram

--

--

Brayden Cloud
Hello Asterisk.

Software Engineer at Asterisk and Google SWE Intern