DevChalk

Goravel series

Installation & Project Architecture

Krishan Kumar · · Updated · 8 min

This is the first part of the Goravel series. We are going to build TaskFlow, a task API for teams, and learn Goravel as we go. In this part we plan the database, create the project, start Postgres and Redis in Docker, and get the server running. How to Read DevChalk shows what the code blocks and figures on these pages do.

CodeClone the finished code for this part
Terminal window
git clone https://github.com/krishankumar01/goravel-taskflow
cd goravel-taskflow
git checkout part-01
cp .env.example .env
go run . artisan key:generate
docker compose up -d

The database design

Before we write any code, let’s look at the data. The app needs three tables.

users holds everyone who can log in. Next to the name, email and password, it has a role, which is either manager or member. We use this column for permissions later. A manager can create, edit and delete tasks. A member can only change the status of tasks assigned to them.

column type notes
id bigint primary key
name string
email string unique
password string hashed, never plain
role enum manager or member, default member

tasks is the main table. A task has a title, a description, an optional due date and a status that is one of pending, in-progress or done. It links to the users table twice. created_by is the manager who made the task, and assigned_to is the person who has to finish it. The permission rules use both.

column type notes
id bigint primary key
created_by bigint references users.id
assigned_to bigint references users.id, nullable
title string
description text
status enum pending, in-progress, done, default pending
due_at timestamp nullable, when the task is due

notifications tells a user that something changed. When a task changes, for example when it is assigned to someone, that person gets a row here. read_at is null until the user has seen it.

column type notes
id bigint primary key
user_id bigint references users.id
task_id bigint references tasks.id
message string
read_at timestamp nullable, null means unread

So a user can create many tasks, be assigned many tasks, and have many notifications.

TaskFlow's three tables. Point at a line, or tap it, to highlight the two columns it joins.

Check Go and install the Goravel CLI

You need Go and Docker. Goravel v1.18 needs Go 1.25 or newer. Check what you have:

Terminal window
go version
go version go1.27.3 darwin/arm64

If Go is missing or older, install the current release from go.dev.

Goravel has an installer for new projects, like the Laravel installer. Install it and run it once:

Terminal window
go install github.com/goravel/installer/goravel@latest
goravel

If you see a help screen, it worked. If you see command not found, the Go binary directory is not on your PATH. Add this to your shell config (~/.zshrc or ~/.bashrc), then restart the terminal:

Terminal window
export PATH=$PATH:$(go env GOPATH)/bin

Create the project

The installer creates the project and asks a few questions as it goes:

Terminal window
goravel new
  1. Project name: taskflow.
  2. Version: pick Goravel Lite. It is a slimmer skeleton where you choose only the facades you need. The full version installs everything.
  3. Module name: taskflow. This is the module line in go.mod, and the start of every import path, like taskflow/routes.

It clones the skeleton and downloads the dependencies. Lite starts with only Artisan, Config and Process, so it then runs package:install and asks which facades to add. Press Space to select one and Enter when you are done.

Each facade you pick also installs the facades it depends on, so these four are enough for TaskFlow:

  • Route, to define API routes. It also installs Log, Cache, Hash, Http, RateLimiter, Session, Storage, Validation and View.
  • Auth, for login. It also installs Orm and Schema.
  • Gate, for authorization rules.
  • Queue, for background jobs. It also installs DB.

Some facades need a driver, and the installer asks for each one:

  • Cache: Redis. The memory driver keeps data inside the process and loses it on every restart.
  • Session: Redis, the same as Cache. File works too.
  • Storage: Local. Files on disk are enough until we deploy.
  • Route: Gin. Fiber is also supported, and your code stays almost the same with either.
  • Database: Postgres, for Orm and DB. MySQL, SQL Server and SQLite are also supported.
  • Queue: Redis. Sync runs a job right away, inside the request. Database stores jobs in a table. Redis is the usual choice for a real queue.

When it finishes, go into the project:

Terminal window
cd taskflow

The project structure

If you know Laravel, the layout will look familiar.

  • app/ is the application: controllers, models, jobs, services. It also holds the facades. They are plain files in your project, so you can add your own. Most of our code goes here.
  • bootstrap/app.go is startup: service providers, route registration, migrations.
  • config/ has a file per area: app, auth, cache, database and so on. You rarely edit these directly. They read their values from .env.
  • routes/ holds the route files. It starts with web.go, and we add more in later parts.
  • main.go is the entry point. It boots the app using bootstrap.Boot() and starts the server.
  • taskflow/
    • app/ your code: controllers, models, jobs
      • facades/ Route(), Orm(), Cache() and 17 more, one small file each, yours to edit
      • http/controllers/ request handlers; user_controller.go is a sample
    • bootstrap/
      • app.go wires the app together: providers, route files, migrations
      • providers.go the service providers package:install registered
    • config/ 12 files: app, auth, cache, database, queue… values come from .env
    • database/migrations/ schema changes; Queue’s jobs table is already here
    • resources/views/ HTML templates; welcome.tmpl is the page at /
    • routes/ route files; Goravel loads only the ones app.go calls
      • web.go GET / and GET /users
    • .env your machine’s settings: database, Redis, APP_KEY. Not in Git
    • docker-compose.yml Postgres and Redis, set up in the next section
    • go.mod module taskflow, Goravel v1.18
    • main.go the entry point: boots the app, then starts the HTTP server
The five from the list are in bold.

Postgres and Redis with Docker Compose

We chose Postgres and Redis, so we need both running. The app itself runs with Go on your machine, not inside Docker, because restarts are faster that way. Docker runs only the two services.

The skeleton comes with a docker-compose.yml. Replace what is in it with the two services:

docker-compose.yml
services:
postgres:
image: postgres:15-alpine
restart: always
environment:
POSTGRES_DB: taskflow
POSTGRES_USER: goravel
POSTGRES_PASSWORD: secret
ports:
- "5433:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
redis:
image: redis:alpine
restart: always
ports:
- "6380:6379"
volumes:
- redis_data:/data
volumes:
postgres_data:
redis_data:

The port mapping is host:container. I already have Postgres on 5432 and Redis on 6379 locally, so I map 5433 and 6380 on the host to the standard ports inside the containers. If you have nothing running on those ports, 5432:5432 and 6379:6379 are simpler. If you use those mappings, set DB_PORT=5432 and REDIS_PORT=6379 in .env in the next section.

The named volumes keep the data on disk, so stopping or recreating a container does not wipe the database.

What host:container means. Change the mapping, or turn on "Postgres already installed", and see where the app's connection lands.

Make sure Docker is running, then start them in the background:

Terminal window
docker compose up -d
docker ps

You should see two containers, taskflow-postgres-1 and taskflow-redis-1. The names start with your folder name, so if you cloned the repo they start with goravel-taskflow-. Both should have a STATUS of Up, and the PORTS column should show 0.0.0.0:5433->5432/tcp and 0.0.0.0:6380->6379/tcp, or whatever ports you mapped.

Environment configuration

The installer already made a .env file, and the app reads its settings from it. Change these lines to match docker-compose.yml and leave the rest of the file as it is:

.env
DB_HOST=127.0.0.1
DB_PORT=5433
DB_DATABASE=taskflow
DB_USERNAME=goravel
DB_PASSWORD=secret
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=
REDIS_PORT=6380

Run it

With the containers up, start the app:

Terminal window
go run .
INFO Processing jobs from [redis] connection and [default] queue
GET|HEAD / ................................... taskflow/routes.Web.func1
STATIC public ........................................................
GET|HEAD users ... taskflow/app/http/controllers.(*UserController).Index
Showing [3] routes
[HTTP] Listening on: http://127.0.0.1:3000

It builds the app from main.go, starts a queue worker on Redis and listens on port 3000. It also prints the routes the app has. These three come with the skeleton. Open http://localhost:3000 to see the Goravel welcome page. Press Ctrl+C to stop it.

Goravel has a command line for jobs like migrations and generating code, called Artisan, as in Laravel:

Terminal window
go run . artisan

This lists all the commands, such as db commands, make commands that generate code, and migrate commands.

The server starts even if the database settings are wrong, so check Postgres with one of the db commands:

Terminal window
go run . artisan db:show
PostgreSQL .................................................................
Database .......................................................... taskflow
Host ............................................................. 127.0.0.1
Port .................................................................. 5433
Username ........................................................... goravel
Open Connections ......................................................... 1
Tables ................................................................... 0

It shows the database the app connects to. There are no tables yet. If .env does not match docker-compose.yml, you get connection refused instead.

TaskFlow now runs, with Postgres and Redis behind it. It has no routes of its own yet.

The code for this partgit checkout part-01 Browse the code ↗Download zip