Using Wasp to Build Full-Stack Web Applications on Koyeb
Tags: docker, react, node, tutorial
Published: 2024-05-02 11:06:00

[Wasp](https://wasp-lang.dev/) is a full-stack web framework built to provide a modern take on a Rails-like development experience. Built on top of React, Node.js, and Prisma, Wasp provides the components and structure you need to create well-architected, and feature-rich web experiences.

In this tutorial, we will show how to create and deploy a Wasp application to Koyeb. The application will consist of a server component and a frontend and will be backed by a PostgreSQL database. We will use a Dockerfile to define containers for the two application layers and deploy them using Koyeb's [Dockerfile build feature](https://www.koyeb.com/docs/build-and-deploy/build-from-git#the-dockerfile-build-process).

You can consult the [repository for this guide](https://github.com/koyeb/example-wasp) to follow along on your own. You can deploy the Wasp server process by clicking the [Deploy to Koyeb button](https://www.koyeb.com/docs/build-and-deploy/deploy-to-koyeb-button) below:

[![Deploy to Koyeb](https://www.koyeb.com/static/images/deploy/button.svg)](https://app.koyeb.com/deploy?name=example-wasp-server&type=git&repository=koyeb%2Fexample-wasp&branch=main&builder=dockerfile&target=server-production&instance_type=nano&env%5BDATABASE_URL%5D=CHANGE_ME&env%5BJWT_SECRET%5D=CHANGE_ME&env%5BWASP_SERVER_URL%5D=CHANGE_ME&env%5BWASP_WEB_CLIENT_URL%5D=CHANGE_ME&ports=8000%3Bhttp%3B%2Fapi)

Be sure to modify all of the environment variable values to reflect your own data. You can consult this guide if you need additional information on appropriate values.

Once the server is deployed, you can deploy the frontend with the following deploy button:

[![Deploy to Koyeb](https://www.koyeb.com/static/images/deploy/button.svg)](https://app.koyeb.com/deploy?name=example-wasp-web-app&type=git&repository=koyeb%2Fexample-wasp&branch=main&builder=dockerfile&target=web-app-production&instance_type=nano&env%5BREACT_APP_API_URL%5D=CHANGE_ME&ports=8000%3Bhttp%3B%2F)

Again, be sure to modify the environment variable configuration to match your information.

## Requirements

To successfully follow and complete this guide, you need:

- A [Koyeb account](https://app.koyeb.com) to provision the PostgreSQL database and to build and deploy the Wasp application components.
- [Node.js](https://nodejs.org/en/download) and the `npm` package manager installed on your local computer.

## Steps

To complete this guide and deploy a Wasp application, you'll need to follow these steps:

1. [Install Wasp on your local computer](#install-wasp-on-your-local-computer)
2. [Create a new Wasp project](#create-a-new-wasp-project)
3. [Provision a PostgreSQL database on Koyeb](#provision-a-postgre-sql-database-on-koyeb)
4. [Configure the Wasp project to use the PostgreSQL database](#configure-the-wasp-project-to-use-the-postgre-sql-database)
5. [Create a Dockerfile](#create-a-dockerfile)
6. [Push the project to GitHub](#push-the-project-to-git-hub)
7. [Deploy the Wasp backend service to Koyeb](#deploy-the-wasp-backend-service-to-koyeb)
8. [Deploy the Wasp web app to Koyeb](#deploy-the-wasp-web-app-to-koyeb)

## Install Wasp on your local computer

To get started, you'll need to install Wasp on your local computer.

You can download and run the installation script by typing:

```bash
curl -sSL https://get.wasp-lang.dev/installer.sh | sh
```

The installation script will download the latest Wasp release and install it on your system. You may be prompted to modify one of your shell configuration files to add the Wasp installation directory to your `PATH`.

You can check that the installation was successful by typing:

```bash
wasp version
```

The output should tell you the current version information and provide instructions on how to upgrade or switch to a different version if desired.

## Create a new Wasp project

Now that the Wasp CLI is installed, you can use it to create a new project. We will use a [todo app starter template](https://wasp-lang.dev/docs/project/starter-templates#todo-app-w-typescript) for our project so that we can show how to deploy an application with a database, frontend, and backend.

Create a new project from the template by typing:

```bash
wasp new example-wasp -t todo-ts
```

Navigate into the newly generated project directory and follow the instructions to run the database migrations on the new application:

```bash
cd example-wasp
wasp db migrate-dev
```

It will indicate that the database file cannot be found and create a new one. You may be prompted to enter a name for the new migration.

Once the database is initialized, start the development server by typing:

```bash
wasp start
```

Wasp will build the project and serve it using a basic web server. If you navigate to `http://localhost:3000` in your web browser, you will be redirected to the application's login page. Click the link to sign up for a new account and enter account details. Once authenticated, you'll be taken to a basic todo list application.

If, instead, you navigate to `http://localhost:3001`, you can access the server process.

Press <kbd>CTRL</kbd>-<kbd>C</kbd> to stop the development server when you are finished.

## Provision a PostgreSQL database on Koyeb

Wasp uses a local SQLite during development as a quick and easy way to get started without much overhead. However, for production deployments, Wasp requires a PostgreSQL database. We will use [Koyeb's PostgreSQL service](https://www.koyeb.com/docs/databases) which includes a free tier that we can use to get started.

To deploy a new PostgreSQL database, on the **Overview** tab of the [Koyeb control panel](https://app.koyeb.com/), click **Create Database Service**. Choose a name for the service and choose the region closest to you or your users.

Once the database is provisioned, click the **copy icon** associated with `psql` to save the connection details for later.

## Configure the Wasp project to use the PostgreSQL database

Now that we have an external PostgreSQL database, we can modify our project configuration to use it.

Start by creating an environment file to store the database credentials. The server process is the only component that needs to access the database. It automatically consults variables stored in a `.env.server` file.

Create a `.env.server` file and set `DATABASE_URL` to the connection string you copied from the Koyeb database. Append `?ssl_mode=require` to the end of the connection string since the database requires a secure connection:

```env /<YOUR_DATABASE_CONNECTION_STRING>/
DATABASE_URL=<YOUR_DATABASE_CONNECTION_STRING>?ssl_mode=require
```

Next, open the `main.wasp` file in your project root. In the main `app` configuration, add a `db` key to set the database type to PostgreSQL:

```javascript
app exampleWasp {
  db: {  // [!code ++]
    system: PostgreSQL,  // [!code ++]
  },  // [!code ++]
  wasp: {
    version: "^0.13.0"
  },
  title: "example-wasp",

  auth: {
    userEntity: User,
    methods: {
      usernameAndPassword: {}, // This is a very naive implementation, use 'email' in production instead
      //google: {}, // https://wasp-lang.dev/docs/integrations/google
      //gitHub: {}, // https://wasp-lang.dev/docs/integrations/github
      //email: {} // https://wasp-lang.dev/docs/guides/email-auth
    },
    onAuthFailedRedirectTo: "/login",
  }
}

. . .
```

Remove the existing SQLite migrations and reset the development environment by running `wasp clean`:

```bash
rm -r migrations/
wasp clean
```

Now, rerun the migrations against the PostgreSQL database. Wasp will connect using the details found in the `.env.server` file:

```bash
wasp db migrate-dev
```

Once the migration is complete, you can restart the development server to confirm that everything still functions as expected with PostgreSQL:

```bash
wasp start
```

Again, visit `http://localhost:3000` in your web browser to access the frontend and `http://localhost:3001` to connect to the server process.

Press <kbd>CTRL</kbd>-<kbd>C</kbd> to stop the development server when you are finished.

## Create a Dockerfile

To deploy Wasp to Koyeb, we'll need to build two container images, one with the API backend and another for the static files that serve as the frontend.

Wasp includes a `wasp dockerfile` command that generates a `Dockerfile` automatically that can be used to package build artifacts into a container image. Unfortunately, this process requires you to build the project before you can use the `Dockerfile` and we do not want to store our build output in version control.

Koyeb can build container images from a `Dockerfile` during the deployment process, so we will create our own `Dockerfile` based on the generated one that performs the build as a separate stage and then copies the artifacts to a production images.

Create a `Dockerfile` in the project root directory with the following contents:

```dockerfile
FROM node:20 AS base

# Build the project to generate the .wasp/build output
FROM base AS wasp-builder
WORKDIR /wasp
ADD . .
RUN curl -sSL https://get.wasp-lang.dev/installer.sh | sh
RUN /root/.local/bin/wasp build

# Build the server
FROM base AS server-builder
RUN apt update && apt install --yes build-essential python3 libtool autoconf automake && rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY --from=wasp-builder /wasp/.wasp/build/src ./src
COPY --from=wasp-builder /wasp/.wasp/build/package.json .
COPY --from=wasp-builder /wasp/.wasp/build/package-lock.json .
COPY --from=wasp-builder /wasp/.wasp/build/server .wasp/build/server
COPY --from=wasp-builder /wasp/.wasp/build/sdk .wasp/out/sdk
# Install npm packages, resulting in node_modules/.
RUN npm install && cd .wasp/build/server && npm install
COPY --from=wasp-builder /wasp/.wasp/build/db/schema.prisma .wasp/build/db/
RUN cd .wasp/build/server && npx prisma generate --schema='../db/schema.prisma'
# Building the server should come after Prisma generation.
RUN cd .wasp/build/server && npm run bundle

# Build the web app
FROM wasp-builder AS web-app-builder
ARG REACT_APP_API_URL
ENV REACT_APP_API_URL=$REACT_APP_API_URL
RUN npm ci
WORKDIR /wasp/.wasp/build/web-app
RUN npm ci && REACT_APP_API_URL=$REACT_APP_API_URL npm run build

# Run the server component
FROM base AS server-production
RUN apt update && apt install --yes python3 && rm -rf /var/lib/apt/lists/*
ENV NODE_ENV production
WORKDIR /app
COPY --from=server-builder /app/node_modules ./node_modules
COPY --from=server-builder /app/.wasp/out/sdk .wasp/out/sdk
COPY --from=server-builder /app/.wasp/build/server/node_modules .wasp/build/server/node_modules
COPY --from=server-builder /app/.wasp/build/server/bundle .wasp/build/server/bundle
COPY --from=server-builder /app/.wasp/build/server/package*.json .wasp/build/server/
COPY --from=server-builder /app/.wasp/build/server/scripts .wasp/build/server/scripts
COPY --from=wasp-builder /wasp/.wasp/build/db/ .wasp/build/db/
EXPOSE ${PORT}
WORKDIR /app/.wasp/build/server
ENTRYPOINT ["npm", "run", "start-production"]

# Run the web-app
FROM joseluisq/static-web-server AS web-app-production
ARG PORT
ENV SERVER_PORT=$PORT
COPY --from=web-app-builder /wasp/.wasp/build/web-app/build /public
```

This `Dockerfile` uses a [multi-stage build](https://docs.docker.com/build/building/multi-stage/) to perform various steps in different contexts. Let's take a look at the stages it defines and what they do:

- `wasp-builder`: This stage installs Wasp and runs the build process to generate the build artifacts. Parts of the output produced by this command will be copied over to subsequent stages.
- `server-builder`: This stage builds the Wasp backend code. It begins by installing some system-level dependencies and then copying over the components the server requires from the `wasp-builder` stage. It runs two separate `npm install` commands to install all of the project dependencies, generates the database schema, and then bundles the server project files.
- `web-app-builder`: This stage builds the front end web app. Because the generated files are static, we need to pass in the URL where the backend will be deployed as `REACT_APP_API_URL` during this process. Again, we perform two `npm install` commands (one for the larger project and one for the front end specifically) and build the web app project.
- `server-production`: This stage runs the server process. It installs some system-level runtime dependencies and then copies all of the required build artifacts from the `server-builder` and `wasp-builder` stages. It then runs the server process in production mode.
- `web-app-production`: This stage uses a static web server to serve the web app files generated by the `web-app-builder` stage.

Using this `Dockerfile`, we can build runnable container images for our project by building the `server-production` and `web-app-production` targets. These will contain everything we need to run our application in production.

## Push the project to GitHub

We now have everything we need to build and run our Wasp application in production. Next we need to commit our changes to git and push them to a GitHub repository.

When we initialized a new project, Wasp automatically generated an appropriate `.gitignore` file. Initialize a new git repository in the project root directory by typing:

```bash
git init
```

Once you're ready, add the project files to the staging area and commit them. [Create a new GitHub repository](https://github.com/new) and then run the following commands to commit and push changes to your GitHub repository:

```bash /<YOUR_GITHUB_USERNAME>/ /<YOUR_REPOSITORY_NAME>/
git add :/
git commit -m "Initial commit"
git remote add origin git@github.com:<YOUR_GITHUB_USERNAME>/<YOUR_REPOSITORY_NAME>.git
git branch -M main
git push -u origin main
```

**Note:** Make sure to replace `<YOUR_GITHUB_USERNAME>` and `<YOUR_REPOSITORY_NAME>` with your GitHub username and repository name.

## Deploy the Wasp backend service to Koyeb

Now that the project code is on GitHub, we can deploy our components to Koyeb. We will start by deploying the server backend.

On the **Overview** tab of the [Koyeb control panel](https://app.koyeb.com/), click **Create Web Service** to begin:

1. Select **GitHub** as the deployment method.
2. Select your Wasp project repository repository. Alternatively, you can enter our public [Wasp example repository](https://github.com/koyeb/example-wasp) into the **Public GitHub repository** field at the bottom of the page: `https://github.com/koyeb/example-wasp`.
3. In the **Builder** section, select **Dockerfile**. Click the **Override** toggle associated with **Target** and enter `server-production` in the field.
4. In the **App and Service names** section at the bottom, choose a name for your App and Service. Your App name will be a component of the public URL for your Service, using the following format: `https://<YOUR_APP_NAME>-<YOUR_KOYEB_ORG>.koyeb.app`.
5. In the **Exposed ports** section, change the **Path** for the configured port from `/` to `/api`.
6. In the **Environment variables** section, click **Bulk edit** to enter multiple environment variables at once. In the text box that appears, paste the following:

   ```
   DATABASE_URL=
   WASP_SERVER_URL=
   WASP_WEB_CLIENT_URL=
   JWT_SECRET=
   ```

   Set the variable values to reference your own information as follows:

   - `DATABASE_URL`: Use the value you from your `.env.server` file. This should be the PostgreSQL connection string with `?ssl_mode=require` appended to the end.
   - `WASP_SERVER_URL`: This is the URL where the server backend will be deployed, including the `/api` path. Set it using the following format: `https://<YOUR_APP_NAME>-<YOUR_KOYEB_ORG>.koyeb.app/api`.
   - `WASP_WEB_CLIENT_URL`: This is the URL where the web frontend will be deployed. We will deploy the web frontend to the web root (`/`), so fill this in using the following format: `https://<YOUR_APP_NAME>-<YOUR_KOYEB_ORG>.koyeb.app`.
   - `JWT_SECRET`: This is the JSON web token (JWT) used during authentication. Set this to a randomly generated string. You can generate an appropriate string by typing `openssl rand -base64 24` in your terminal.

7. Click **Deploy**.

Koyeb will pull the GitHub repository and build the `server-production` target of the `Dockerfile` within.

Once the deployment is healthy, visit your Koyeb Service's subdomain (you can find this on your Service's detail page). It will have the following format:

```
https://<YOUR_APP_NAME>-<KOYEB_ORG_NAME>.koyeb.app/api
```

You will see the "hello world" message from the server backend.

## Deploy the Wasp web app to Koyeb

With the backend service deployed, we can now create a new Service for the web application.

On the **Apps** tab of the [Koyeb control panel](https://app.koyeb.com/apps), click on the Wasp server backend App. In the upper-right corner, click **Create Service** to create a new Service within the same application domain.

1. Select **GitHub** as the deployment method.
2. Select your Wasp project repository repository. Alternatively, you can enter our public [Wasp example repository](https://github.com/koyeb/example-wasp) into the **Public GitHub repository** field at the bottom of the page: `https://github.com/koyeb/example-wasp`.
3. In the **Builder** section, select **Dockerfile**. Click the **Override** toggle associated with **Target** and enter `web-app-production` in the field.
4. In the **Environment variables** section, click **Add variable** to add the following variable:

   - `REACT_APP_API_URL`: Set this to the URL for your backend service. This should be the same value you used for `WASP_SERVER_URL` in the backend configuration. It will have the following format: `https://<YOUR_APP_NAME>-<YOUR_KOYEB_ORG>.koyeb.app/api`.

5. Click **Deploy**.

Koyeb will pull the GitHub repository and build the `web-app-production` target of the `Dockerfile` within.

Once the deployment is healthy, visit the web application's Koyeb subdomain (you can find this on your Service's detail page). It will have the following format:

```
https://<YOUR_APP_NAME>-<KOYEB_ORG_NAME>.koyeb.app/
```

As before, you will be redirected to the application's login page. Click to link to sign up to create a new account. After authenticating, you will be able to access the todo list functionality as before.

## Conclusion

In this guide, we demonstrated how to build and deploy a Wasp application to Koyeb. We started with one of Wasp's templates to create a working, full-stack web application backed by a database. We migrated the application's configuration from a local SQLite database to an external PostgreSQL database to prepare for deployment. Afterwards, we created a multi-stage `Dockerfile` to build and configure our various application layers. Finally, we deployed the backend and web app to Koyeb by targeting different stages in the `Dockerfile`.

This tutorial covers the basics of how to manage a Wasp project and deploy to a production environment. As you continue to develop your projects, be sure to check out the [Wasp documentation](https://wasp-lang.dev/docs) to learn how to integrate new features, work with the data model, and leverage the development framework to make your life easier.