Building a Drawing Board with Redwood
Tags: react, javascript
Published: 2021-09-01 13:20:35

---
title: Building a Drawing Board with Redwood
published: true
description: Going back to the drawing board is more fun when you're the one that made the board. In this tutorial, we'll use Redwood to make a drawing board that we can save the drawing of.
tags: #react, #javascript
cover_image: https://cdn.sanity.io/images/5ad74sb4/production/e66164367e53fb5e03ed91518a5c2c00df91ab0a-1000x420.png?w=2000&h=2000&fit=max
---

Sometimes you just want to be able to draw pictures to describe things and send them to others quickly. You don't want to use a whole service or deal with specific tools, you just want to hand draw a thing and send it. That's what we're going to make in this Redwood tutorial.

We'll make a drawing board app that saves drawings to a Postgres database so users can quickly download their drawings as images.

## Setting up Postgres locally

Since we'll be working with a Postgres database, let's start by making a local instance we can connect to. You can [download Postgres here](https://www.postgresql.org/download/) for the OS you're working on.

While you're going through the initial set up, pay attention to the user name and password you create. You'll need those for the connection string in Redwood.

## Setting up the Redwood app

Now go to a terminal and run:

```bash
yarn create redwood-app drawing-board
```

This will create a new Redwood app with a bunch of directories and files. Feel free to check everything out, but we'll focus mainly on the `api` and `web` directories. One quick thing we need to handle is updating that database connection string.

Inside the `.env` file, uncomment the `DATABASE_URL` line and change it to match your local instance. That might look something like:

```bash
DATABASE_URL=postgres://postgres:admin@localhost:5432/drawing_board
```

This will be referenced in the `schema.prisma` file we'll be updating, but as a sneak peek, here's where it's used. You don't have to change anything here.

```javascript
datasource db {
  provider = "postgresql"
  url = env("DATABASE_URL")
}
```

We'll need this in place so that the back-end of the app can communicate with the database.

## Making the models

Let's write the model for the table that will hold the images. In `api > db`, open `schema.prisma` and the first thing we'll update is the `provider` value. This should be `postgresql` instead of `sqlite`.

Next, we'll remove the example user model from the file and replace it with our own. This model will represent the data we want to store for each canvas image we capture.

```javascript
model Capture {
  id   Int    @id @default(autoincrement())
  name String
  url  String
}
```

All we're doing is storing a name and URL string for the image. We'll be storing a data url for the image in Postgres, but this could also be a link to an online storage service like AWS or Cloudinary.

### Run the migration

Since this is the only model we'll have for this app, it's safe to run the migration now:

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

You'll be prompted to give the migration a name, but everything else happens automatically. Now that the database is set up, we'll start working on the back-end.

## Making the back-end

Redwood uses GraphQL to handle everything in the back-end. So we're going to need some types and resolvers to let the front-end make request and get the data to the database. We'll run this command to generate some types and resolvers for us based on the `Capture` model.

```bash
yarn rw g sdl capture
```

Redwood creates new folders and files that have the GraphQL types in `api > src > graphql` and a query resolver in `api > src > services` for the `Capture` model. Now we just have to modify a few things.

### Adding the create mutation for captures

We need to add new type to give us the ability to save captures from the drawing board to the database. Then we'll have to add the resolver that will actually add the data to the database.

First, we'll go to the `captures.sdl.js` file in the `api > src > graphql` directory. We're just going to add a new mutation type below the `UpdateCaptureInput`.

```javascript
type Mutation {
  createCapture(input: CreateCaptureInput!): Capture!
}
```

This will make the request accessible from the front-end. Next we'll go to the `captures.js` file and add the mutation that persists the data to the database right below the query.

```javascript
export const createCapture = ({ input }) => {
  return db.capture.create({
    data: input,
  })
}
```

We have a fully functioning GraphQL server right now! In fact, if you run the app with `yarn rw dev` and go to `http://localhost:8911/graphql` in your browser, you'll see the GraphQL explorer with the mutation we just added.

![graphql explorer](https://res.cloudinary.com/jesse-thisdot/image/upload/v1627080633/e-603fc55d218a650069f5228b/lf7gtzannverxsfmqzid.png)

## Making the front-end

Since we have the back-end working, it's finally time to build the front-end. We'll have a canvas on the page that let's use capture the drawing with a button click. At the bottom of the page, we'll have small views of the existing captured drawings.

### The drawing board

Let's start by making a page for the drawing board. We'll take advantage of another Redwood command for this.

```bash
yarn rw g page capture /
```

This generates the page component, a [Storybook](https://storybook.js.org/) story, and a Jest test for the component. It'll also automatically add the route for the new page to `Routes.js`. This page will point be at the base URL of the app, which is why we have the `/` int the page creation command above.

We also need to install the `fabric` package so that we can work with the canvas. In a terminal, go to the `web` directory and run:

```bash
yarn add fabric
```

_Make sure you go up one directory level in your terminal after this! That way you'll be running the commands in the right place._

Then we'll go to `web > src > pages > CapturePage` and edit the `CapturePage.js` file. You can delete everything inside of the `CapturePage` component because we won't be using any of it. Let's import a few things first.

```javascript
import React, { useState, useEffect } from 'react'
import { fabric } from 'fabric'
```

Next, we'll add a new state to the `CapturePage` component that will initialize the canvas object for us so that we can draw and save images.

```javascript
const [canvas, setCanvas] = useState('')

useEffect(() => {
  setCanvas(initCanvas())
}, [])

const initCanvas = () =>
  new fabric.Canvas('capture', {
    height: 500,
    width: 500,
    backgroundColor: '#F6F6F6',
    isDrawingMode: true,
  })
```

The `initCanvas` sets up a new instance of the fabric canvas object and it's targeting a canvas element with an id of `capture`. Then we've given it some dimensions to limit the size on the page. It has a background color so that users know that's it's a different element to interact with. Lastly, we have drawing mode enabled so users can draw on the canvas and we can save the drawing they make.

We use `useEffect` to initialize this canvas only when the page loads and we set the state so that we can access this canvas later.

We'll add the canvas element inside a fragment in the return statement.

```javascript
return (
  <>
    <canvas id="capture" />
  </>
)
```

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

![initial drawing board](https://res.cloudinary.com/jesse-thisdot/image/upload/v1627080602/e-603fc55d218a650069f5228b/iddnbgihxyor9fsjpxho.png)

### Saving drawings

Next we need to add that button to trigger the save action. We'll need to add another import so we can use our GraphQL create mutation.

```javascript
import { useMutation } from '@redwoodjs/web'
```

Then we'll need to write the definition of the GraphQL request we want to make. Below the import we just added, write the following:

```javascript
const CREATE_CAPTURE_MUTATION = gql`
  mutation CreateCaptureMutation($input: CreateCaptureInput!) {
    createCapture(input: $input) {
      id
    }
  }
`
```

This defines the `createCapture` mutation we'll use to save the drawings. Now that we have the definition, we need to create the mutation request. Inside of the `CapturePage` component, just above the capture state, add:

```javascript
const [createCapture] = useMutation(CREATE_CAPTURE_MUTATION)
```

Using this hook gives us access to a function we can call for this mutation. Now we should define a method that gets called when the save button is clicked. Below the `initCanvas` call, add this:

```javascript
const saveCapture = () => {
  const imageUrl = canvas.lowerCanvasEl.toDataURL()

  const input = {
    name: `Capture${Math.random().toString()}`,
    url: imageUrl,
  }

  createCapture({
    variables: { input },
  })
}
```

If you take a look at the way Fabric handles canvases, there's an inner canvas that we want to get to we can save the drawing as an image. So once we have the right canvas, we get the data URL for the image and save it as part of the mutation input.

There are many better ways to make a random name for an image, but we're using a random number to give them unique names. Then we call the mutation with the input we just defined and it saves the drawing the database!

### Displaying drawings

Just so you see that the drawings are actually there, scribble two or three things and save them. Now we'll add a query to get all of the images and show them in a flex element.

Let's start by adding a new hook to an existing import.

```javascript
import { useMutation, useQuery } from '@redwoodjs/web'
```

Then we'll add a query for all the captures right below the mutation.

```javascript
const GET_CAPTURES = gql`
  query {
    captures {
      name
      url
    }
  }
`
```

This will return an array with all of the captures we have in the database. We'll make this definition into something we can use inside of the `CapturePage` component.

```javascript
const { data } = useQuery(GET_CAPTURES)
```

Lastly, we'll add an element that displays all of the returned captures. This can go right below the `<canvas>` element.

```javascript
<div style={{ display: 'flex' }}>
  {data?.captures &&
    data.captures.map((capture) => (
      <img
        key={capture.name}
        style={{ padding: '24px', height: '100px', width: '100px' }}
        src={capture.url}
      />
    ))}
</div>
```

It maps over all of the captures and displays them as small images on the page. So you'll see something similar to this.

![saved drawings being displayed](https://res.cloudinary.com/jesse-thisdot/image/upload/v1627080541/e-603fc55d218a650069f5228b/xavi98fuv73k1tfwmpl3.png)

That's it! Now you can tweak this to have a sleek interface or a fancier way of storing images.

## Finished code

You can check out the front-end in this [Code Sandbox](https://codesandbox.io/s/purple-butterfly-eo54n) or you can look at the full code in the [`drawing-board` folder of this repo](https://github.com/flippedcoder/blog-examples/tree/main/drawing-board).

If you check out the Code Sandbox, you'll notice the code is a little different. This is because we can't run a Redwood mono-repo with this tool, so an example of how the front-end works is what you'll see in the Code Sandbox. To get the full code, it's better to take a look at the repo on GitHub.

## Conclusion

Since a lot of the things we do are virtual, it helps to have ways to still interface with the web that feels more natural. Drawing boards are becoming more popular so hopefully, this is helpful!