Making a Meal Picker with Redwood
Tags: react, javascript, graphql
Published: 2021-10-07 20:37:25

---
title: Making a Meal Picker with Redwood
published: true
description: Feeding yourself as an adult is hard sometimes. There are times it's too hard to make a choice on what to eat because there are too many options. That's why we're going to make an app to pick a meal for us!
tags: #react, #javascript, #graphql
cover_image: https://cdn.sanity.io/images/5ad74sb4/production/f4379e6632d59266f7092ac29d46ff229a9cd109-1000x420.png?w=2000&h=2000&fit=max
---

Deciding what to eat every day is something that a lot of us struggle with. It'd be so much easier if there was a button you could push and it would choose one of your favorite meals for you.

That's why we're going to make a meal picker in this tutorial. We'll use Redwood to create the front-end and back-end of the app which will let us save recipes and choose a random meal. We'll also be using Cloudinary to host the video we have associated with the recipes.

## Setting up the app

To get started, let's make a new Redwood app that uses TypeScript. In a terminal, run this command.

```bash
yarn create redwood-app --typescript meal-picker
```

This will generate a lot of new files and folders in the `meal-picker` directory. The main two directories are `web` and `api`. The `web` folder contains all of the code for the front-end and the `api` directory contains all of the code for the back-end.

Most times, it's a good practice to start building the data model of a new app first.

## Building the back-end

We'll start by opening the `prisma.schema` file in `api > db` directory. This file holds all of the models for the tables we'll have in the database. We'll be using a local Postgres instance, so if you need to download that you can find the right version [here](https://www.postgresql.org/download/).

The first thing we'll do is update the `provider` value to `postgresql`. Next, we need to create a `.env` file in the root of the project. You'll see the `DATABASE_URL` being read from the environment below the type of database we're using. In the `.env` file, add the connection string to your local Postgres instance. It might look similar to this.

```env
DATABASE_URL=postgres://postgres:admin@localhost:5432/meal_picker
```

### Creating the model

Now you have everything set to connect to your database. We need to define the model for the meals we'll be saving to choose from. You can delete the `UserExample` model and replace it with this.

```javascript
model Meal {
  id      String @id @default(cuid())
  title   String
  recipe  String
  video   String
}
```

This model defines a table that will hold all of the details for our meals. There has to be a way for users to input their favorite meals and all of the details. With the model ready, we can go ahead and run the migration to get these changes on the database.

```bash
yarn rw prisma migrate dev
```

That will define the `Meal` table in Postgres. So now we can use one of the Redwood commands to generate this CRUD functionality for us.

## Creating the meal collection management functionality

We'll need to have both the GraphQL back-end in place and the front-end in place to allow users to add, edit, and delete meals. This normally takes a bit of time, but the following command generates all of that for us.

```bash
yarn rw g scaffold meal
```

Check the `api > src > graphql` directory and you should see `meals.sdl.ts` file that has all of the types for the queries and mutations we need. If you check in `api > src > services > meals`, you'll see three files. Two of them are for tests and the `meals.ts` file holds all of the resolvers for our queries and mutations. These already connect to the database so we have our entire back-end created!

## Moving to the front-end

Now that we have the GraphQL server ready to go, let's take a look at the files the `scaffold` command created on the front-end. In `web > src > pages > Meals`, you'll see several new directories that correspond to different views of the CRUD for meals.

If you take a look in `web > src > components > Meal`, you'll see a number of components that we created. These components interact with and display the data that we get from the back-end. It's worth taking the time to peek at these different files and see how they work, but you can still run the app and see all of the CRUD in action without ever looking at the code.

In your terminal, run this command and navigate to `http://localhost:8910/meals`.

```bash
yarn rw dev
```

You should see something like this in your browser.

![how the meals table looks with no entries](https://res.cloudinary.com/jesse-thisdot/image/upload/v1631654470/e-603fc55d218a650069f5228b/bgpn7e5mtzxpo4madk2l.png)

Now add a few entries by clicking the "New Meal" button. This will bring up a new page and let you add the details.

![new meal page](https://res.cloudinary.com/jesse-thisdot/image/upload/v1631654448/e-603fc55d218a650069f5228b/xdeywkzh1xiwg8f47pqh.png)

If you haven't uploaded any videos for your meals, take a second to go to [your Cloudinary account](https://cloudinary.com/users/register/free) and add those. Make sure you grab the URLs for the videos you want to use because you'll add them to your new meal entries.

Once you've added a few meals, you should see a table that lists all of your entries.

![table with meal entries](https://res.cloudinary.com/jesse-thisdot/image/upload/v1631654434/e-603fc55d218a650069f5228b/fmtlss3c16gzn2r9bgvs.png)

That's everything we need to handle the CRUD functionality for our meals. You might think of this as an admin area in the app. Redwood generated all of this for us with just one command. All that's left is making the random meal picker page.

## Making the picker page

Let's make a new page for the picker. We'll use another Redwood command.

```bash
yarn rw g page picker /
```

This updates our routes to make the picker page the root page and it generates some files for us. Go to `web > src > pages > PickerPage` and open `PickerPage.tsx`. This is where we'll make the button that will tell us what to eat.

We'll get rid of a lot of the boilerplate code in the `PickerPage` component. Let's start by adding the import statements for the methods we'll be using. So your list of import statements should look like this.

```javascript
import { useQuery } from '@redwoodjs/web'
import { MetaTags } from '@redwoodjs/web'
import { useState } from 'react'
```

Now let's add the GraphQL query we need to get all of the meals we have available to choose from. This goes right below the import statements.

```javascript
const GET_MEALS = gql`
  query {
    meals {
      title
      recipe
      video
    }
  }
`
```

One more quick thing before we start using this query. Since this is a TypeScript app, let's add the type for a single meal. Below the query just wrote, add the `Meal` type.

```javascript
interface Meal {
  title: string
  recipe: string
  video: string
}
```

When we get ready to work with the meals data, now we know exactly what to expect. Now we get to delete a lot of code. Inside the `PickerPage` component, delete everything except the `<MetaTags>` element. Your `PickerPage.tsx` should look like this now.

```javascript
import { useQuery } from '@redwoodjs/web'
import { MetaTags } from '@redwoodjs/web'
import { useState } from 'react'

const GET_MEALS = gql`
  query {
    meals {
      title
      recipe
      video
    }
  }
`

interface Meal {
  title: string
  recipe: string
  video: string
}

const PickerPage = () => {
  return (
    <>
      <MetaTags
        title="Picker"
      />
    </>
  )
}

export default PickerPage
```

All that's left is adding the data and the elements to display it.

### Handling the data

Let's add a new `meal` state in the component. We'll use our `Meal` type to define what values are expected.

```javascript
const [meal, setMeal] = useState<Meal>()
```

Next we'll use the `useQuery` hook to fetch our data from the GraphQL server.

```javascript
const { loading, data } = useQuery(GET_MEALS)
```

We get both the data and a loading state for it. That way we can account for any latency in the request and show the users a loading screen. If we don't handle this, the app will likely crash because the data isn't available yet. Right below the `useQuery` call, we'll add the code to handle this loading state.

```javascript
if (loading) {
  return <div>Loading...</div>
}
```

The last function we need to add before creating the elements to show our random meal will actually be responsible for choosing that meal. When we click a button on the screen, it'll call this function and set the `meal` state to some random selection.

```javascript
const loadMeal = () => {
  if (data.meals.length !== 0) {
    const max = data.meals.length
    const index = getRandomInt(0, max)
    setMeal(data.meals[index])
  }
}
```

There is a tiny helper function we for to get that random integer. Add this code below the `PickerPage` component.

```javascript
function getRandomInt(min, max) {
  min = Math.ceil(min);
  max = Math.floor(max);
  return Math.floor(Math.random() * (max - min) + min);
}
```

We have the data in place and all of the accompanying functions we need. Let's finally add the elements to display everything.

### The button and meal display

Below the `<MetaTags>` element, add these elements.

```javascript
<h1>{meal ? meal.title : 'Find out what you are going to eat'}</h1>
<button onClick={loadMeal} style={{ fontSize: '18px', padding: '24px 32px', width: '500px' }}>Tell me what to eat</button>
```

The text on the page will change based on whether or not you've had a random meal selected. Then there's the button with a few styles on it that will call the function to choose a new random meal.

If you run the app again with `yarn rw dev`, you'll see something like this in your browser.

![meal selector button](https://res.cloudinary.com/jesse-thisdot/image/upload/v1631654385/e-603fc55d218a650069f5228b/y6wduwb9h5e8lism4tdj.png)

The last piece of code is to display the info for the randomly selected meal. We'll do this with a conditional render statement below the `<button>`.

```javascript
{meal &&
  <>
    <p>{meal.recipe}</p>
    <video src={meal.video} controls height='350' width='500'></video>
  </>
}
```

This will display the recipe and the video whenever the button is clicked and a meal is selected. Now if you look in the browser, you should see something like this.

![page after the button has been clicked and a meal has been selected](https://res.cloudinary.com/jesse-thisdot/image/upload/v1631654321/e-603fc55d218a650069f5228b/ubxf52p3joar5y65pwvc.png)

That's it! You now have a meal picker that will make it hard for you to say you can't decide what to eat anymore.

## Finished code

If you want to check out the finished front-end and back-end code, check out the code in [the `food-picker` folder of this repo](https://github.com/flippedcoder/blog-examples/tree/main/food-picker). You can see an example of the front-end in [this Code Sandbox](https://codesandbox.io/s/dawn-cherry-5i9rr).

## Conclusion

Not every project you work on has to be super detailed. Sometimes you just need something to prove a concept or you want to make something for yourself. I know I've definitely used this app to pick what I'm going to eat more than I want to admit.