DEV Community

Aodhan Hamilton
Aodhan Hamilton

Posted on

Accelerate your learning by starting with the full source code of my first project

This is my very first project and I'm giving the code away to help other beginners to accelerate their learning.

mvp

This article will guide you through setting up and deploying a production-ready app and utilizes a Create-React-App (CRA) boilerplate to get us at a good starting point and Strapi for building our api end-points and auth and a Postgres DB for storing data.

The idea behind it is that it's a drinks ordering app. Users can order drinks off a menu on their phone. It's intended to be used in-house and then the orders apear on a bar management interface. Super simple app, but the concepts of this app can be used to create virtually anything, after all every app boils down to a C.R.U.D (Create. Read. Update. Delete) app.

There are three repo's that complete this app, two CRA repos and one a strapi repo. All repos are hosted as their own app on Digital Ocean's App platform, in a Microservice architecture-like setup.

The ordering (drinks-user) and the management (drinks-bar) sides of the app, build off of Create-React-App, with the drinks-app using Chakra-ui for a small bit of functionality/ animation.

I will provide the CRA repos and I encourage you to check out Thew Dhanat's article to set up and host your own Strapi app.

Don't forget to install Postgresql on the project with npm install pg before deploying on Digital Ocean and you need to create a production db separately to add it as an existing one, instead of the dev db in Thew's article if you want to do a full production build.

My repo's will be linked at the bottom of the article.

Strapi is fairly straight-forward, you generate the code visually and it's easy to get set-up with their quick-start docs.

If you need Strapi help, I strongly recommend Alex the Entreprenerd on YouTube and he has a great course on Udemy too and/or you can join Strapi's Forum or slack channel you can find through their site.

The app for people to submit orders (drinks-user) is just a form and to manage state, I'm using React-easy-state

src/state/OrderStore

import { store } from "@risingstack/react-easy-state";

const OrderStore = store({
  items: [],
  TableNum: "",
});

export default OrderStore;
Enter fullscreen mode Exit fullscreen mode

I'm then importing the state whenever I need to use it, as you can see at the top of src/components/UIForm
and then the initial render, I'm getting the data from the Strapi endpoint e.g ${baseUrl}/item, with useEffect and then pushing each Item from my the request into the empty item array of the state, by pushing in a for loop on the initial fetch.

import React, { useEffect } from "react";
import Item from "./Item";
import axios from "axios";
import OrderStore from "../state/OrderStore";
import { view } from "@risingstack/react-easy-state";
const UIForm = view(({ items }) => {
  let baseUrl = process.env.REACT_APP_BASEURL;
  const getMenu = async () => {
    const menuItems = await axios.get(`${baseUrl}/items`);
    for (let i = 0; i < menuItems.data.length; i++) {
      OrderStore.items.push({
        itemName: menuItems.data[i].name,
        id: menuItems.data[i].id,
        checked: false,
      });
    }
  };

  useEffect(() => {
    getMenu();
  }, []);

... 
Enter fullscreen mode Exit fullscreen mode

Here I'm rendering a UIForm component with as many item components from src/components/item for many items that we get back from the endpoint.

let setInput = (e) => {
    OrderStore.TableNum = e.target.value;
  };

  let setOrder = async (e) => {
    e.preventDefault();

    for (let i = 0; i < OrderStore.items.length; i++) {
      if (OrderStore.items[i].checked === true) {
        const res = await fetch(`${baseUrl}/orders`, {
          method: "POST",
          headers: {
            "Content-Type": "application/json",
          },
          body: JSON.stringify({
            drink: OrderStore.items[i].itemName,
            tableNum: OrderStore.TableNum,
          }),
        });
        const data = await res.json();
        console.log(data);
      } //If statement ends
      OrderStore.items[i].checked = false;
      console.log(
        `${OrderStore.items[i].itemName} is ${OrderStore.items[i].checked} `
      );
    } // For statement ends
    OrderStore.TableNum = "";
  };

  return (
    <div className="item-container">
      <form onSubmit={setOrder}>
        {OrderStore.items.map((item) => (
          <Item
            key={item.id}
            name={item.itemName}
            checked={item.checked}
            value={item.itemName}
            id={item.id}
          />
        ))}

        <div className="submit-div">
          <label htmlFor="tableNum" className="table-label">
            Table Number
          </label>
          <input
            type="text"
            name="tableNum"
            value={OrderStore.TableNum}
            onChange={setInput}
          ></input>
          <button>Order</button>
        </div>
      </form>
    </div>
  );
});
export default UIForm;
Enter fullscreen mode Exit fullscreen mode

I'm using the Axios package to fetch data, which the official Strapi docs recommend and I prefer it to the browser fetch Api tbh. The view() wrapping the component is from the state library and assures it re-renders when the state is updated.

user-repo

Above you'll see my folder structure for this repo and I recommend creating a .env file in the root directory, like I have, to save an environment variable to ensure changing the URL of the hosted app is a breeze and you only need to change it in one place, I've also set it up to work that way if you're cloning my project.

Environment variables are the way you can save api and secret keys for your projects in the same repo without uploading sensitive information to your git repos, by telling your .gitignore file, in the root directory, to ignore the file extension when uploading to git.

gitignorefile

You'll need to prefix environment variables for React apps, with REACT_APP_ for it to work.

My CRA's .env's both have the following environment variables

//Strapi URL
REACT_APP_BASEURL=http://localhost:1337 
Enter fullscreen mode Exit fullscreen mode

1337 is the port that Strapi runs on in development mode and you'll notice that's the port Thew gets us to set the port of our Digital ocean app to. It's not really needed in development mode, but we've set it up for deployment, by using setting it to be storing it in a variable in our code

let baseUrl = process.env.REACT_APP_BASEURL;
Enter fullscreen mode Exit fullscreen mode

Now when we deploy the repo, we can set the environment variable through Digital Ocean and it will look something like this
REACT_APP_BASEURL= https://drinks-backend-{you-unique-id}.ondigitalocean.app

You'll need to make sure you change the keys in the setOrder function of the body, where you make the post request, to be the same as the headers of the Strapi collection... see below

 body: JSON.stringify({
            drink: OrderStore.items[i].itemName,
            tableNum: OrderStore.TableNum,
          })
Enter fullscreen mode Exit fullscreen mode

feilds

You'll need to do a similar thing in pages that make requests in the drinks-bar app.

Currently, the way it is, anyone can make an order and auth is only on the management app. The login page makes the auth request and stores it in state of the useContext and therefore the whole app will be able to use the user credentials.

I deployed these as separate apps on Digital Oceans App platform and to do so yourself, you can go through a similar process to Thew. Select the Repo >> Select Static site (for the CRA's)

static

Repeat for the other repo and done!

My Repo's

drinks-user (the order form)
drinks-bar (the bar management app)

Follow me on Twitter to give me feedback and let me know what you end up building.

Thanks for visiting my first ever piece of content!

Top comments (0)