Making RedwoodJS Easier with VSCode Dev Containers
Tags: redwoodjs, javascript, tutorial, docker
Published: 2022-07-08 17:01:00

In this short (but wordy) guide, I would like to show you how you can set up a RedwoodJS application using Postgres to run in a VSCode Dev Container or Github Codespaces. 

But first, if you're not sure what RedwoodJS is, it's an open-source full-stack web framework that makes building applications extremely easy. Think of Ruby on Rails, but all in JavaScript using libraries you might already be familiar with and extremely easy to set up. You can check it out here [https://redwoodjs.com/docs/introduction](https://redwoodjs.com/docs/introduction).

RedwoodJS is easy to set up and start, but what would make it even better? Making it extremely easy to get your developer environment set up and running with a couple of clicks and compatible with Github Codespaces. This short guide will give you a template needed to do just that!

We will use the Visual Studio Code Dev Container extension/feature to accomplish this goal. It's a fantastic tool I use almost daily to run my projects in what I like to think of as a development sandbox. Since each project runs in its own sandbox, it doesn't have to worry about installing different Ruby, Rails, NodeJS, Elixir, or Erlang to get my project up and running on my computer. So let's get to it.

## Required To Get Started

- Docker for Desktop
- Visual Studio Code

## Set Up Dev Container!

Alright, to get started, open up your RedwoodJS project in VSCode. If you don't have one, it's as simple as.

```jsx
yarn create redwood-app ./super-awesome-project-name
```

Now let's install the `Remote Development` extension. It should look like the following image.

![VS Code Remote Development](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yw73rn2l09xp7djz3zdu.png)

The `Remote Development` extension package contains everything we would need and more to use Docker as a remote development environment.

Now let's start generating our development container configuration. Bring up the command palette using `Command + Shift + P` on Mac and `Ctrl + Shift + P` on Windows.  Type "add dev" and select `Add Development Container Configuration Files.`

![Command palette with add dev typed in](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rxv01x1z8m9hy3s1r1m7.png)

You will see a list of pre-built definitions. Well, go with the `Node.js & PostgreSQL` since it is already almost everything we need to get RedwoodJS up and running.

![Prompt with Node.js & Postgres highlights](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/79ne2tg8g4oj6tg15w0d.png)

You will next be asked which version of Node.js you would like your sandbox to run. Let's stick with the default.

![Node version selection prompt with default selected](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/iv9s0wvpu1llpplk0304.png)

The last window allows you to install additional packages/features, but clicking "OK" in the top right will be good enough.

![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/flyk69grusw076z1s8oy.png)

You should now see a new folder in your project called `.devcontainer`. Inside it will be 4 files, `devconatiner.json`, `docker-compose.yml`, `create-db-user.sql` and `Dockerfile`. 


![Display of files that will be generated](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/15pdvdyc7cxs8tcsq9qt.png)

Well, ignore `create-db-user.sql` since it's unimportant for this guide. We also won't be touching the `Dockerfile` but it's what Docker will use to build a development environment with all bells and whistles we need to run Node.js and more.

Let's open up `docker-compose.yml` file. We will need to make a minor tweak to it. If you dont know what `docker-compose` is, it's a tool to tell Docker how to step up containers that will run services we would like. Usually, most web development projects need 2-3 services. One service is your web server, RedwoodJS, for us, and another service would be a database like Postgres.

Out of the box, RedwoodJS will use SQLite, but most of the time, I find myself using Postgres in production, and wouldn't it be cool to emulate production? Using Development Container makes it super easy to use Postgres locally, also. So let's try making our local environment run.  

Our new `docker-compose.yml` already comes pre-configured with an `app` container we will use to develop inside. Looking closely, you will see this container is based on our `Dockerfile` image. It also has a `db` container to run our development Postgres database. We need to add one more thing, a container to run our test database. I have annotated the code below to add a `db-test` container and corresponding volumes. You might be able to copy and paste the whole thing.

```yaml
version: '3.8'

services:
  app:
    # Using a Dockerfile is optional, but included for completeness.
    build:
      context: .
      dockerfile: Dockerfile
      args:
        # Update 'VARIANT' to pick an LTS version of Node.js: 18, 16, 14.
        # Append -bullseye or -buster to pin to an OS version.
        # Use -bullseye variants on local arm64/Apple Silicon.
        VARIANT: 16-bullseye

    volumes:
      # This is where VS Code should expect to find your project's source code and the value of "workspaceFolder" in .devcontainer/devcontainer.json
      - ..:/workspace:cached

      # Uncomment the next line to use Docker from inside the container. See https://aka.ms/vscode-remote/samples/docker-from-docker-compose for details.
      # - /var/run/docker.sock:/var/run/docker.sock

    # Overrides default command so things don't shut down after the process ends.
    command: sleep infinity

    # Runs app on the same network as the service container, allows "forwardPorts" in devcontainer.json function.
    network_mode: service:db

    # Use "forwardPorts" in **devcontainer.json** to forward an app port locally.
    # (Adding the "ports" property to this file will not forward from a Codespace.)

    # Uncomment the next line to use a non-root user for all processes - See https://aka.ms/vscode-remote/containers/non-root for details.
    # user: vscode

    # Uncomment the next four lines if you will use a ptrace-based debugger like C++, Go, and Rust.
    # cap_add:
    #   - SYS_PTRACE
    # security_opt:
    #   - seccomp:unconfined

  # You can include other services not opened by VS Code as well
  db:
    image: postgres:latest
    restart: unless-stopped
    volumes:
      - postgres-data:/var/lib/postgresql/data
    environment:
      POSTGRES_USER: postgres
      POSTGRES_DB: development_db
      POSTGRES_PASSWORD: postgres
  ###### START ADDED SECTION: Create a container to run the test database
  db-test:
    image: postgres:latest
    restart: unless-stopped
    volumes:
      - postgres-test-data:/var/lib/postgresql/data
    environment:
      POSTGRES_USER: postgres
      POSTGRES_DB: test_db
      POSTGRES_PASSWORD: postgres
  ###### END ADDED SECTION
volumes:
  postgres-data:
  postgres-test-data:#### ADDED LINE
```

Now let's open up your `.devcontainer.json` file. This file is specifically for VSCode. It tells it how to run our development container. More specially, the docker-compose file to use, which container we will use to do development inside, and the extension we would like to install by default. The extension configuration is super lovely, or anyone who works with you will have the same extension automatically installed to make their dev life more effortless. Finally, you can copy and paste the code below into the file.

```yaml
{
	"name": "RedwoodJS & Postgres (Community)",

	// Update the 'dockerComposeFile' list if you have more compose files or use different names.
	"dockerComposeFile": "docker-compose.yml",

	// The 'service' property is the name of the service for the container that VS Code should
	// use. Update this value and .devcontainer/docker-compose.yml to the real service name.
	"service": "app",

	// The optional 'workspaceFolder' property is the path VS Code should open by default when
	// connected. This is typically a volume mount in .devcontainer/docker-compose.yml
	"workspaceFolder": "/workspace",

	// Configure tool-specific properties.
	"customizations": {
		// Configure properties specific to VS Code.
		"vscode": {
			// Add the IDs of extensions you want installed when the container is created.
			"extensions": [
				"redwoodjs.redwood",
				"dbaeumer.vscode-eslint",
				"ofhumanbondage.react-proptypes-intellisense",
				"mgmcdermott.vscode-language-babel",
				"editorconfig.editorconfig",
				"prisma.prisma",
				"graphql.vscode-graphql"
			]
		}
	},

	// Uncomment the next line if you want to keep your containers running after VS Code shuts down.
	// "shutdownAction": "none",

	// Uncomment the next line to use 'postCreateCommand' to run commands after the container is created.
	// "postCreateCommand": "uname -a",

	// Comment out to connect as root instead. To add a non-root user, see: https://aka.ms/vscode-remote/containers/non-root.
	"remoteUser": "node"
}
```

## Postgres In Development

The last phase of our changes is going to be with RedwoodJS. Since we're setting up our local development to run Postgres, we need to update some things in our RedwoodJS.

First, let's update our `schema.prisma` file to run Postgres. Then, update your provider, like in the example below.

```jsx
datasource db {
  provider = "postgresql" // Switched from MySQL to Postgres for local development
  url      = env("DATABASE_URL")
}

generator client {
  provider      = "prisma-client-js"
  binaryTargets = "native"
}

// Define your own datamodels here and run `yarn redwood prisma migrate dev`
// to create migrations for them and apply to your dev DB.
// TODO: Please remove the following example:
model UserExample {
  id    Int     @id @default(autoincrement())
  email String  @unique
  name  String?
}
```

Next, we need to open and update our `.env` file. Since we changed to using Postgres, we need to change `DATABASE_URL` and `TEST_DATABASE_URL` to point to our Postgres database containers and use the credentials that are set in the `docker-compose.yml`. Looking back at the `docker-compose.yml` file, you will see where we got the username, password, and database name. Another important bit is to set the `RWJS_DEV_API_URL` variable so routing happens properly from within our container to the GQL server.

```yaml
# THIS FILE SHOULD NOT BE CHECKED INTO YOUR VERSION CONTROL SYSTEM
#
# Environment variables set here will override those in .env.defaults.
# Any environment variables you need in production you will need to setup with
# your hosting provider. For example in Netlify you can add environment
# variables in Settings > Build & Deploy > environment
#
DATABASE_URL=postgres://postgres:postgres@localhost:5432/development_db
TEST_DATABASE_URL=postgres://postgres:postgres@localhost:5432/test_db
#
# Sets an app-specific secret used to sign and verify your own app's webhooks.
# For example if you schedule a cron job with a signed payload that later will
# then invoke your api-side webhook function you will use this secret to sign and the verify.
# Important: Please change this default to a strong password or other secret
# WEBHOOK_SECRET=THIS_IS_NOT_SECRET_PLEASE_CHANGE
# Set API base URL to be localhost, not 0.0.0.0
RWJS_DEV_API_URL=http://localhost
```

## Start It Up!

Now we're in the home stretch. Let's get your project running in its Development Container. Bring up the command palette using `Command + Shift + P` on Mac and `Ctrl + Shift + P` on Windows. Type in "reopen in container" and select "Reopen in Container" option. 


![Command palette open with Reopen in Container typed in](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vp5bheem4j99oqwp3f4b.png)

This last step will take a little while. It will be downloading the Docker images for your development environment and Postgres, then building it. So sit tight, grab a cup of tea, and get a stretch break-in.

Once done, your VSCode should open up to what looks like a typical project, but with one secret. Your VSCode is connected to your docker container and running inside. The area you will notice the most significant difference will be the terminal.

![Preview of the terminal](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/47o9tglv9tzt3b5k3xqu.png)

It says you are inside the `workspace` folder, and your user is `node`. If you were to change directories and move up one, you would see completely different folders on your regular computer. You are now in your developer sandbox. Now run `yarn redwood dev` to start your server and see the magic happen. A bonus to all this work is that it will also work in Github Codespaces.

If there is anything still fuzzy or like me to elaborate further, let me know. I will make sure to do so.