shahanneda / s23-bootcamp-pfrm-graphql

S23 UW Blueprint Bootcamp Repository for PFRM-GraphQL Stack

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Bootcamp: Python + Flask + React + MongoDB (GraphQL Backend)

This repo contains the starter-code for our bootcamp activity.

Tech Stack

We will be using the Python + Flask + React + MongoDB stack. These technologies are easy to learn, allow for fast development speeds, and are popularly used in Blueprint.

  • Frontend: React + Redux
  • Backend: Python + Flask + Apollo Server
  • Database: MongoDB

Setup

The dev environment is containerized with Docker to minimize setup efforts.

  1. Install Docker Desktop (skip tutorials): MacOS | Windows (Home) | Windows (Pro, Enterprise, Education) | Linux
# verify your installation by running the following in a terminal
$ docker --version
$ docker-compose --version
  1. Clone this repo and go into the project directory
$ git clone https://github.com/uwblueprint/bootcamp-pfrm-graphql.git
$ cd bootcamp-pfrm-graphql
  1. Set up your MongoDB database (your local server will connect to this, to persist data)

a. Create a MongoDB Atlas account and sign-in

b. Create a new project called blueprint-bootcamp (you may need to create an organization first)

c. Click "Build a Database" > "Shared" (the free option) > "Create"

d. Keep all default options on the "Create a cluster" page and click "Create a cluster". Wait 1-3 minutes for your cluster to be provisioned

e. When your cluster is ready, click "Connect"

f. Select "Add your current IP" and confirm

g. Create a database user and a password (avoid special characters). Please remember this password.

  1. Edit your backend code to connect with the MongoDB database a. In /backend/.env.sample, replace <username> in the MG_DATABASE_URL string with the database username from the previous step.

b. In the MG_DATABASE_URL string, replace <password> with the password you set earlier, and replace <dbname> with bootcamp

c. Rename /backend/.env.sample to /backend/.env

d. In backend/app/models/__init__.py, set erase_db_and_sync to true on line 10. This will populate your database with some fake data we created upon start up

  1. Fill in frontend environment variables in /frontend/.env.sample, then rename the file to /frontend/.env
REACT_APP_GRAPHQL_SERVER_URL="http://localhost:5005/"
  1. Run the application
$ docker-compose up --build
  1. Set erase_db_and_sync back to false.

  2. Go to http://localhost:3000 in your browser. You should see this:

Complete setup

  1. Go to http://localhost:5005/graphql in your browser and verify the playground is up and running:

Confirm backend is up

To first setup the application run the following command:

$ docker-compose up --build

On subsequent runs you can omit the --build tag

$ docker-compose up

Keep in mind that both the frontend and backend have hot reload enabled so you will rarely need to restart the application.

Warm Up

Feel free to skip this section if you've had some experience working with GraphQL before.

Let's try running our first GraphQL mutation by adding a new restaurant into our directory. GraphQL is a query language and a mutation is an operation used to write data.

Navigate to http://localhost:5005/graphql in a browser, this is our GraphQL Playground. Click on the "DOCS" tab on the right and you should see a couple of "function" signatures. Click on RootMutation and then createRestaurant(...): CreateRestaurant to see its full signature. To create a restaurant, we simply need to call createRestaurant(...) and supply some arguments containing our new restaurant's info. The return value will be our new restaurant. Note the ! in the type declaration of the restaurant parameter. It means restaurant is a non-nullable parameter, so we must provide a value.

This is the syntax for a GraphQL mutation:

mutation {
  createRestaurant(restaurant: {name: "Lazeez Shawarma", address: "160 University Ave W #2, Waterloo, ON N2L 3E9", description: "Shawarma and Falafels", type: "Middle East", budget: "LOW", rating: 2}) {
    newRestaurant {
      id
      name
      address
      description
      type
      budget
      description
      rating
    }
  }
}

The purpose of the first 2 lines should be pretty clear. What about the rest? The fields enclosed in the braces following createRestaurant(restaurant: {name: "Lazeez Shawarma", address: "160 University Ave W #2, Waterloo, ON N2L 3E9", description: "Shawarma and Falafels", type: "Middle East", budget: "LOW", rating: 2}) specify the fields of the created restaurant object that we would like to retrieve. In this case, we want to retrieve all available fields (you can view the available fields in the "DOCS" tab).

Now, type the mutation out in the left panel of the GraphQL Playground (don't use copy & paste). You'll notice there's syntax highlighting and auto-completion! Press the run button to run the mutation.

Next, let's try running a GraphQL query. Open "DOCS" again and click on RootQuery and then getRestaurantById(...): Restaurant. What do you need to do in order to retrieve the restaurant you just created?

Your Tasks

Following the starter-code's pattern, complete these tasks:

Currently, our restaurant directory maintains a singular list of restaurants. Suppose we now want the ability to create restaurant groups (e.g. "Uni Plaza", "Cafes", "Open Late", etc.).

A single group can contain multiple restaurants, and a single restaurant can be part of multiple groups.

A RestaurantGroup model should have these fields: name, description, restaurantIds*

* implementation dependent, but the RestaurantGroup needs some way of keeping track of its members. Hint: look into Sequelize associations.

  1. Using the existing code as a template, create a GraphQL query and mutation for RestaurantGroup, enabling create and retrieve operations.

    a. Sample createRestaurantGroup mutation:

    mutation {
        createRestaurantGroup(name: "Open Late", description: "Late Night Snack Restaurants", restaurantIds: [645b05286f45f7dcaac617b7, 645b05296f45f7dcaac617b8, 645b05296f45f7dcaac617b9]) {
            id
            name
            description
            restaurants # can return restaurantIds for now, but we will be expanding on this shortly
        }
    }

    b. Sample getRestaurantGroupById query:

    query {
        getRestaurantGroupById(id: "645b05286f45f7dcaac617b7") {
            id
            name
            description
            restaurants # can only restaurantIds for now, but we will be expanding on this shortly
        }
    }
  2. Display RestaurantGroup data in the frontend (try to reuse existing components to save time, don't worry about design and appearance)

  3. If you chose to only return restaurantIds previously, modify your solution so that your query and mutation responses include full restaurant information (i.e. member restaurants' names, ratings, descriptions, etc.)

Tip

For some help with debugging your work, you can use the GraphQL Playground (http://localhost:5005/graphql). It is a browser-based GraphQL IDE providing syntax-highlighting, auto-completion, and documentation! The Warm Up gives a quick walk-through of this tool.

Please ASK FOR HELP if you're ever stuck!

Extensions

  1. To support updating a RestaurantGroup, create another mutation called updateRestaurantGroup.
mutation {
    updateRestaurantGroup(id: "645b05286f45f7dcaac617b7", name: "Edited Name", description: "Edited Description", restaurantIds: [1, 2]) {
        id
        name
        description
        restaurants
    }
}
  1. To support deleting a RestaurantGroup, create another mutation called deleteRestaurantGroup. Note we are not selecting fields because the return value is just an ID.
mutation {
    deleteRestaurantGroup(id: "645b05286f45f7dcaac617b7")
}
  1. Modify the deleteRestaurant mutation logic so that deleting a restaurant will result in its removal from all the groups that it was part of

About

S23 UW Blueprint Bootcamp Repository for PFRM-GraphQL Stack


Languages

Language:Python 52.6%Language:JavaScript 39.2%Language:HTML 4.4%Language:CSS 2.4%Language:SCSS 0.7%Language:Dockerfile 0.7%