Skip to content

Commit

Permalink
Add an example with PostgreSQL and Docker Compose. (#112)
Browse files Browse the repository at this point in the history
Resolves #99.
  • Loading branch information
jsthomas authored Jul 6, 2021
1 parent 581ecaa commit cac2db7
Show file tree
Hide file tree
Showing 12 changed files with 235 additions and 1 deletion.
1 change: 1 addition & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ jobs:
with:
ocaml-version: ${{matrix.ocaml}}

- run: opam depext --yes conf-postgresql
- run: opam depext --yes conf-libev
- run: opam install --yes --deps-only --with-test .
- run: opam exec -- dune runtest
Expand Down
1 change: 1 addition & 0 deletions dream.opam
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ depends: [
# Testing, development.
"alcotest" {with-test}
"bisect_ppx" {with-test & >= "2.5.0"} # --instrument-with.
"caqti-driver-postgresql" {with-test}
"caqti-driver-sqlite3" {with-test}
"crunch" {with-test}
"lambdasoup" {with-test}
Expand Down
4 changes: 3 additions & 1 deletion example/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,9 @@ if something is missing!
 —  benchmarks sending WebSocket messages quickly.
- [**`w-multipart-dump`**](w-multipart-dump#files)  —  echoes
`multipart/form-data` bodies for debugging.
- [**`w-docker-postgres`**](w-docker-postgres#files)
 —  illustrates how to run Dream and
PostgreSQL in Docker containers using Docker Compose.
- [**`z-playground`**](z-playground#files)  —  source code of
the Dream playground.

Expand Down Expand Up @@ -187,7 +190,6 @@ Techniques:
- `w-graphql-sql`
- `w-graphql-mutation`
- `w-https-redirect`
- `w-postgres-docker`
- `w-sql-stream`
- `w-websocket-stream`

Expand Down
13 changes: 13 additions & 0 deletions example/w-docker-postgres/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# esy build environment
_esy/

# esy dependencies
node_modules/

# git
.git/
.gitignore

# Development
Dockerfile
docker-compose.yml
24 changes: 24 additions & 0 deletions example/w-docker-postgres/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
FROM ocaml/opam:alpine as build

# Install system dependencies
RUN sudo apk add --update libev-dev openssl-dev libpq postgresql-dev

WORKDIR /home/opam

# Install dependencies
ADD app.opam app.opam
RUN opam install . --deps-only

# Build project
ADD . .
RUN opam exec -- dune build



FROM alpine:3.12 as run

RUN apk add --update libev libpq postgresql-dev

COPY --from=build /home/opam/_build/default/app.exe /bin/app

ENTRYPOINT /bin/app
83 changes: 83 additions & 0 deletions example/w-docker-postgres/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# `w-docker-postgres`

<br>

This example illustrates how to use
[Docker](https://en.wikipedia.org/wiki/Docker_(software)) and
[docker-compose](https://docs.docker.com/compose/) to manage a simple
web server with a Postgres database.

Running a database under docker can simplify the process of setting up
a development environment. This is especially true if your application
uses several databases or services and you want a convenient way to
manage them all with a single tool.

The example app allows users to see a list of existing comments and
post new comments. Take a look at this
[example](https://github.com/aantron/dream/tree/master/example/h-sql#files)
for more background information.

## Getting Set Up

Start your docker containers by running `docker-compose up -d`. It
will take some time to build the `web` container; subsequent rebuilds
are faster.

When both containers are available, you should see:
```
Creating network "w-docker-postgres_default" with the default driver
Creating w-docker-postgres_postgres_1 ... done
Creating w-docker-postgres_web_1 ... done
```

At this point the PostgreSQL database is ready but doesn't have any
tables. Fix that by running:

```
docker-compose exec postgres psql -U dream -c "$(cat schema.sql)"
```

This will create the `comment` and `dream_session` tables described in
`schema.sql`.

Finally, open your browser to
[`http://localhost:8080/`](http://localhost:8080/) to try the
application.


## Tips

If you modify `app.eml.ml`, you will need to run
```
docker-compose build && docker-compose up web
```
to rebuild the `web` container and see your changes.

To view the logs from the API container (rather than having them mixed
in with the logs from the database) run:
```
docker-compose logs web -f
```

The database container contains a copy of the `psql` client. Running

```
docker-compose exec postgres psql -U dream
```

will allow you to start the client and run queries interactively.

<br>

**See also:**

- [**`h-sql`**](../h-sql#files) this example contains essentially the
same application, but uses sqlite.
- [**`z-docker-esy`**](../z-docker-esy#files) describes how to deploy
with docker-compose and esy.
- [**`z-docker-opam`**](../z-docker-opam#files) describes how to deploy
with docker-compose and opam.

<br>

[Up to the example index](../#examples)
58 changes: 58 additions & 0 deletions example/w-docker-postgres/app.eml.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
module type DB = Caqti_lwt.CONNECTION
module R = Caqti_request
module T = Caqti_type


let list_comments =
let query =
R.collect T.unit T.(tup2 int string)
"SELECT id, text FROM comment" in
fun (module Db : DB) ->
let%lwt comments_or_error = Db.collect_list query () in
Caqti_lwt.or_fail comments_or_error


let add_comment =
let query =
R.exec T.string
"INSERT INTO comment (text) VALUES ($1)" in
fun text (module Db : DB) ->
let%lwt unit_or_error = Db.exec query text in
Caqti_lwt.or_fail unit_or_error


let render comments request =
<html>
<body>
% comments |> List.iter (fun (_id, comment) ->
<p><%s comment %></p><% ); %>

<%s! Dream.form_tag ~action:"/" request %>
<input name="text" autofocus>
</form>

</body>
</html>


let () =
Dream.run ~interface:"0.0.0.0"
@@ Dream.logger
@@ Dream.sql_pool "postgresql://dream@postgres/dream"
@@ Dream.sql_sessions
@@ Dream.router [

Dream.get "/" (fun request ->
let%lwt comments = Dream.sql request list_comments in
Dream.html (render comments request));

Dream.post "/" (fun request ->
match%lwt Dream.form request with
| `Ok ["text", text] ->
let%lwt () = Dream.sql request (add_comment text) in
Dream.redirect request "/"
| _ ->
Dream.empty `Bad_Request);

]
@@ Dream.not_found
8 changes: 8 additions & 0 deletions example/w-docker-postgres/app.opam
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
opam-version: "2.0"

depends: [
"ocaml" {>= "4.08.0"}
"dune" {>= "2.0.0"}
"dream"
"caqti-driver-postgresql"
]
20 changes: 20 additions & 0 deletions example/w-docker-postgres/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
version: "3"

services:
postgres:
image: postgres
environment:
- POSTGRES_USER=dream
- POSTGRES_DB=dream

web:
build: .
expose:
- 8080
ports:
- "8080:8080"
links:
- postgres
restart: always
logging:
driver: ${LOGGING_DRIVER:-json-file}
12 changes: 12 additions & 0 deletions example/w-docker-postgres/dune
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
(executable
(name app)
(libraries caqti-driver-postgresql dream)
(preprocess (pps lwt_ppx))
)

(rule
(targets app.ml)
(deps app.eml.ml)
(action (run dream_eml %{deps} --workspace %{workspace_root})))

(data_only_dirs _esy esy.lock)
1 change: 1 addition & 0 deletions example/w-docker-postgres/dune-project
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
(lang dune 2.0)
11 changes: 11 additions & 0 deletions example/w-docker-postgres/schema.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
CREATE TABLE comment (
id SERIAL PRIMARY KEY,
text TEXT NOT NULL
);

CREATE TABLE dream_session (
id TEXT PRIMARY KEY,
label TEXT NOT NULL,
expires_at REAL NOT NULL,
payload TEXT NOT NULL
);

0 comments on commit cac2db7

Please sign in to comment.