All tutorials
Tutorial

Using Wasp to Build Full-Stack Web Applications on Koyeb

13 min

Introduction

Wasp 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.

You can consult the repository for this guide to follow along on your own. You can deploy the Wasp server process by clicking the Deploy to Koyeb button below:

Deploy to Koyeb

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

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 to provision the PostgreSQL database and to build and deploy the Wasp application components.
  • Node.js 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
  2. Create a new Wasp project
  3. Provision a PostgreSQL database on Koyeb
  4. Configure the Wasp project to use the PostgreSQL database
  5. Create a Dockerfile
  6. Push the project to GitHub
  7. Deploy the Wasp backend service to Koyeb
  8. 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 the following. This guide was written to target Wasp version v0.13.2. If you are using a different version of Wasp, the functionality and specifically the Dockerfile creation process may be different:

curl -sSL https://get.wasp-lang.dev/installer.sh | sh -s -- --version 0.13.2

The installation script will download the given 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:

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 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:

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:

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:

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 CTRL-C 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 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, 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:

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:

app exampleWasp {
  db: {  
    system: PostgreSQL,  
  },  
  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:

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:

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:

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 CTRL-C 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:

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 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:

git init

Once you're ready, add the project files to the staging area and commit them. Create a new GitHub repository and then run the following commands to commit and push changes to your GitHub repository:

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, 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 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, 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 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 to learn how to integrate new features, work with the data model, and leverage the development framework to make your life easier.

Koyeb

Welcome to Koyeb

Koyeb is a developer-friendly serverless platform to deploy any apps globally.

  • Start for free, pay as you grow
  • Deploy your first app in no time
Start for free
The fastest way to deploy applications globally.