Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions waspc/ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## Unreleased

### 🎉 New Features

- You can now specify which PostgreSQL image to use in `wasp start db` with the `--db-image` argument. ([#3182](https://github.com/wasp-lang/wasp/pull/3182))

### 📖 Documentation

- Added note for SMTP ports being blocked by some hosting providers (by @Vickram-T-G). ([#3109](https://github.com/wasp-lang/wasp/pull/3109))
Expand Down
34 changes: 20 additions & 14 deletions waspc/cli/exe/Main.hs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ main = withUtf8 . (`E.catch` handleInternalErrors) $ do
("new" : newArgs) -> Command.Call.New newArgs
("new:ai" : newAiArgs) -> Command.Call.NewAi newAiArgs
["start"] -> Command.Call.Start
["start", "db"] -> Command.Call.StartDb
("start" : "db" : startDbArgs) -> Command.Call.StartDb startDbArgs
["clean"] -> Command.Call.Clean
["ts-setup"] -> Command.Call.TsSetup
["compile"] -> Command.Call.Compile
Expand Down Expand Up @@ -104,7 +104,7 @@ main = withUtf8 . (`E.catch` handleInternalErrors) $ do
projectConfigJson
_unknownCommand -> printWaspNewAiUsage >> exitFailure
Command.Call.Start -> runCommand start
Command.Call.StartDb -> runCommand Command.Start.Db.start
Command.Call.StartDb startDbArgs -> runCommand $ Command.Start.Db.start startDbArgs
Command.Call.Clean -> runCommand clean
Command.Call.TsSetup -> runCommand tsConfigSetup
Command.Call.Compile -> runCommand compile
Expand Down Expand Up @@ -176,7 +176,9 @@ printUsage =
cmd " uninstall Removes Wasp from your system.",
title " IN PROJECT",
cmd " start Runs Wasp app in development mode, watching for file changes.",
cmd " start db Starts managed development database for you.",
cmd " start db [--db-image <image>]",
" Starts managed development database for you.",
" Optionally specify a custom Docker image.",
cmd " db <db-cmd> [args] Executes a database command. Run 'wasp db' for more info.",
cmd " clean Deletes all generated code, all cached artifacts, and the node_modules dir.",
" Wasp equivalent of 'have you tried closing and opening it again?'.",
Expand Down Expand Up @@ -220,7 +222,7 @@ printVersion = do
dbCli :: [String] -> IO ()
dbCli args = case args of
-- These commands don't require an existing and running database.
["start"] -> runCommand Command.Start.Db.start
"start" : optionalStartArgs -> runCommand $ Command.Start.Db.start optionalStartArgs
-- These commands require an existing and running database.
["reset"] -> runCommandThatRequiresDbRunning Command.Db.Reset.reset
"migrate-dev" : optionalMigrateArgs -> runCommandThatRequiresDbRunning $ Command.Db.Migrate.migrateDev optionalMigrateArgs
Expand All @@ -238,21 +240,25 @@ printDbUsage =
" wasp db <command> [command-args]",
"",
title "COMMANDS",
cmd " start Alias for `wasp start db`.",
cmd " reset Drops all data and tables from development database and re-applies all migrations.",
cmd " seed [name] Executes a db seed function (specified via app.db.seeds).",
" If there are multiple seeds, you can specify a seed to execute by providing its name,",
" or if not then you will be asked to provide the name interactively.",
cmd $ intercalate "\n" [
" migrate-dev Ensures dev database corresponds to the current state of schema(entities):",
" - Generates a new migration if there are changes in the schema.",
" - Applies any pending migrations to the database either using the",
" supplied migration name or asking for one.",
" start [--db-image <image>] Alias for `wasp start db`.",
" Starts managed development database for you.",
" Optionally specify a custom Docker image."
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not the same as for wasp start db (missing "with --db-image). I would add it here or remove it up there.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't get this comment, can you clarify? I do handle the argument in both cases.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Aha sorry, I should have been more verbose! So CLI docs for wasp start db are very similar, but a bit different, and there is no reason for them to be different, so I was suggesting to make them the same. THe difference is in "with --db-image" part).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ahh understood, my eyes were skipping over that haha

],
cmd " reset Drops all data and tables from development database and re-applies all migrations.",
cmd " seed [name] Executes a db seed function (specified via app.db.seeds).",
" If there are multiple seeds, you can specify a seed to execute by providing its name,",
" or if not then you will be asked to provide the name interactively.",
cmd $ intercalate "\n" [
" migrate-dev Ensures dev database corresponds to the current state of schema(entities):",
" - Generates a new migration if there are changes in the schema.",
" - Applies any pending migrations to the database either using the",
" supplied migration name or asking for one.",
" OPTIONS:",
" --name [migration-name]",
" --create-only"
],
cmd " studio GUI for inspecting your database.",
cmd " studio GUI for inspecting your database.",
"",
title "EXAMPLES",
" wasp db migrate-dev",
Expand Down
2 changes: 1 addition & 1 deletion waspc/cli/src/Wasp/Cli/Command/Call.hs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ data Call
= New Arguments
| NewAi Arguments
| Start
| StartDb
| StartDb Arguments
| Clean
| Uninstall
| TsSetup
Expand Down
40 changes: 34 additions & 6 deletions waspc/cli/src/Wasp/Cli/Command/Start/Db.hs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ where
import Control.Monad (when)
import qualified Control.Monad.Except as E
import Control.Monad.IO.Class (liftIO)
import Data.Function ((&))
import Data.Maybe (isJust)
import qualified Options.Applicative as Opt
import StrongPath (Abs, Dir, File', Path', Rel, fromRelFile)
import System.Environment (lookupEnv)
import System.Process (callCommand)
Expand All @@ -16,38 +18,62 @@ import qualified Wasp.AppSpec as AS
import qualified Wasp.AppSpec.App.Db as AS.App.Db
import qualified Wasp.AppSpec.Valid as ASV
import Wasp.Cli.Command (Command, CommandError (CommandError))
import Wasp.Cli.Command.Call (Arguments)
import Wasp.Cli.Command.Common (throwIfExeIsNotAvailable)
import Wasp.Cli.Command.Compile (analyze)
import Wasp.Cli.Command.Message (cliSendMessageC)
import Wasp.Cli.Command.Require (InWaspProject (InWaspProject), require)
import Wasp.Cli.Util.Parser (parseArguments)
import Wasp.Db.Postgres (defaultDockerImageForPostgres)
import qualified Wasp.Message as Msg
import Wasp.Project.Common (WaspProjectDir, makeAppUniqueId)
import Wasp.Project.Db (databaseUrlEnvVarName)
import qualified Wasp.Project.Db.Dev.Postgres as Dev.Postgres
import Wasp.Project.Env (dotEnvServer)
import Wasp.Util (whenM)
import Wasp.Util.Docker (DockerImageName)
import qualified Wasp.Util.Network.Socket as Socket

-- | Starts a "managed" dev database, where "managed" means that
-- Wasp creates it and connects the Wasp app with it.
-- Wasp is smart while doing this so it checks which database is specified
-- in Wasp configuration and spins up a database of appropriate type.
start :: Command ()
start = do
start :: Arguments -> Command ()
start args = do
startDbArgs <-
parseArguments "wasp start db" startDbArgsParser args
& either (E.throwError . CommandError "Invalid arguments") return
Comment on lines +42 to +45
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to change anything here, just FYI.

I rarely use & in Haskell. I believe it's more idiomatic doing stuff from right to left (which is unfortunate) so I'm not used to seeing this.

Here's how I would probably write this:

start args = do
  startDbArgs <-
    either (E.throwError . CommandError "Invalid arguments") return $
      parseArguments "wasp start db" startDbArgsParser args

Or even like this:

start args = do
  startDbArgs <- case parseArguments "wasp start db" startDbArgsParser args of
    Left err -> E.throwError $ CommandError "Invalid arguments" err
    Right parsedArgs -> return parsedArgs

You can pick whatever you like the most, all options are perfectly valid.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Opposite opinion: I think the current code with the & is the best one.

While & is not that popular as $, I don't see any reason why one would not use it in appropriate places("appropriate" being the key here).

In Haskell, you anyway read both from left to right and right to left, it depends on the situation, so there is no golden rule here.

It does make sense to stick with how people normally write Haskell, so I agree one shoudln't go crazy with &, but here I think its usage is perfect, because it allows you to read code in the right order -> business logic is in focus, error handling comes after (and is even nicely indented). So I can read it as do this, but if error this and I think that is perfect.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this case it's written like that for consistency, like in here:

createNewProject :: Arguments -> Command ()
createNewProject args = do
newProjectArgs <-
parseArguments "wasp new" newProjectArgsParser args
& either Common.throwProjectCreationError return

I think a good idea in general for this would be to start using Options.Applicative for all of our CLI argument parsing, not just occasionally.


InWaspProject waspProjectDir <- require
appSpec <- analyze waspProjectDir

throwIfCustomDbAlreadyInUse appSpec

let (appName, _) = ASV.getApp appSpec

case ASV.getValidDbSystem appSpec of
AS.App.Db.SQLite -> noteSQLiteDoesntNeedStart
AS.App.Db.PostgreSQL -> startPostgreDevDb waspProjectDir appName
AS.App.Db.PostgreSQL -> startPostgresDevDb waspProjectDir appName (dbImage startDbArgs)
where
noteSQLiteDoesntNeedStart =
cliSendMessageC . Msg.Info $
"Nothing to do! You are all good, you are using SQLite which doesn't need to be started."

startDbArgsParser :: Opt.Parser StartDbArgs
startDbArgsParser =
StartDbArgs
<$> Opt.strOption
( Opt.long "db-image"
<> Opt.metavar "IMAGE"
<> Opt.help "Docker image to use for the database"
<> Opt.showDefault
<> Opt.value defaultDockerImageForPostgres
)

data StartDbArgs = StartDbArgs
{ dbImage :: DockerImageName
}

throwIfCustomDbAlreadyInUse :: AS.AppSpec -> Command ()
throwIfCustomDbAlreadyInUse spec = do
throwIfDbUrlInEnv
Expand Down Expand Up @@ -82,8 +108,8 @@ throwIfCustomDbAlreadyInUse spec = do
throwCustomDbAlreadyInUseError msg =
E.throwError $ CommandError "You are using custom database already" msg

startPostgreDevDb :: Path' Abs (Dir WaspProjectDir) -> String -> Command ()
startPostgreDevDb waspProjectDir appName = do
startPostgresDevDb :: Path' Abs (Dir WaspProjectDir) -> String -> String -> Command ()
startPostgresDevDb waspProjectDir appName dbDockerImage = do
throwIfExeIsNotAvailable
"docker"
"To run PostgreSQL dev database, Wasp needs `docker` installed and in PATH."
Expand All @@ -94,6 +120,7 @@ startPostgreDevDb waspProjectDir appName = do
[ "✨ Starting a PostgreSQL dev database (based on your Wasp config) ✨",
"",
"Additional info:",
" ℹ Using Docker image: " <> dbDockerImage,
" ℹ Connection URL, in case you might want to connect with external tools:",
" " <> connectionUrl,
" ℹ Database data is persisted in a docker volume with the following name"
Expand All @@ -117,7 +144,7 @@ startPostgreDevDb waspProjectDir appName = do
"--env POSTGRES_PASSWORD=%s",
"--env POSTGRES_USER=%s",
"--env POSTGRES_DB=%s",
"postgres"
"%s"
]
)
dockerContainerName
Expand All @@ -126,6 +153,7 @@ startPostgreDevDb waspProjectDir appName = do
Dev.Postgres.defaultDevPass
Dev.Postgres.defaultDevUser
dbName
dbDockerImage
liftIO $ callCommand command
where
dockerVolumeName = makeWaspDevDbDockerVolumeName waspProjectDir appName
Expand Down
5 changes: 5 additions & 0 deletions waspc/src/Wasp/Db/Postgres.hs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
module Wasp.Db.Postgres
( makeConnectionUrl,
postgresMaxDbNameLength,
defaultDockerImageForPostgres,
)
where

import Text.Printf (printf)
import Wasp.Util.Docker (DockerImageName)

makeConnectionUrl :: String -> String -> Int -> String -> String
makeConnectionUrl user pass port dbName =
Expand All @@ -13,3 +15,6 @@ makeConnectionUrl user pass port dbName =
-- As specified by PostgreSQL documentation.
postgresMaxDbNameLength :: Int
postgresMaxDbNameLength = 63

defaultDockerImageForPostgres :: DockerImageName
defaultDockerImageForPostgres = "postgres"
3 changes: 3 additions & 0 deletions waspc/src/Wasp/Util/Docker.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module Wasp.Util.Docker (DockerImageName) where

type DockerImageName = String
1 change: 1 addition & 0 deletions waspc/waspc.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,7 @@ library
Wasp.Util.Aeson
Wasp.Util.Control.Monad
Wasp.Util.Debug
Wasp.Util.Docker
Wasp.Util.Fib
Wasp.Util.FilePath
Wasp.Util.HashMap
Expand Down
26 changes: 26 additions & 0 deletions web/docs/data-model/databases.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,32 @@ Also, make sure that:
In case you might want to connect to the dev database through the external tool like `psql` or [pgAdmin](https://www.pgadmin.org/), the credentials are printed in the console when you run `wasp db start`, at the very beginning.
:::

##### Customising the dev database {#custom-database}

You can also specify a custom Docker image to use for the database with the `--db-image` option. This is particularly useful when you need PostgreSQL with specific extensions (like PostGIS for geographic data, pgvector for embeddings, etc.), or an older version. By default, Wasp uses the latest official [PostgreSQL Docker image](https://hub.docker.com/_/postgres).

```bash
# Use default PostgreSQL image:
wasp start db
# Same as:
wasp start db --db-image postgres

# Use PostgreSQL version 15:
wasp start db --db-image postgres:15

# Use PostgreSQL with PostGIS extension for geographic data:
wasp start db --db-image postgis/postgis:14-3.2

# Use PostgreSQL with pgvector extension for AI embeddings:
wasp start db --db-image pgvector/pgvector:pg16
```

:::note

The custom Docker image you specify must use the `POSTGRES_DB`, `POSTGRES_USER`, and `POSTGRES_PASSWORD` environment variables when configuring the database. Wasp will use those values when connecting to the database. We recommend basing your image on the official [PostgreSQL Docker image](https://hub.docker.com/_/postgres), as it automatically uses these environment variables to set up the database name, user, and password.

:::

#### Connecting to an existing database

If you want to spin up your own dev database (or connect to an external one), you can tell Wasp about it using the `DATABASE_URL` environment variable. Wasp will use the value of `DATABASE_URL` as a connection string.
Expand Down
Loading