DEV Community 👩‍💻👨‍💻

Cover image for Making RedwoodJS Easier with VSCode Dev Containers
Erik Guzman
Erik Guzman

Posted on • Updated on

Making RedwoodJS Easier with VSCode Dev Containers

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.

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.

yarn create redwood-app ./super-awesome-project-name
Enter fullscreen mode Exit fullscreen mode

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

VS Code Remote Development

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

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

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

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

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

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.

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
Enter fullscreen mode Exit fullscreen mode

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.

{
    "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"
}
Enter fullscreen mode Exit fullscreen mode

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.

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?
}
Enter fullscreen mode Exit fullscreen mode

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.

# 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
Enter fullscreen mode Exit fullscreen mode

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

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

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.

Top comments (0)

Visualizing Promises and Async/Await 🤯

async await

☝️ Check out this all-time classic DEV post