diff --git a/docs/asset/sql.png b/docs/asset/sql.png
new file mode 100644
index 0000000..a464918
Binary files /dev/null and b/docs/asset/sql.png differ
diff --git a/docs/web/site/docs.css b/docs/web/site/docs.css
index ed47e55..f01555a 100644
--- a/docs/web/site/docs.css
+++ b/docs/web/site/docs.css
@@ -426,7 +426,7 @@ ul ul li {
}
p + .odoc-spec {
- margin-top: 36px;
+ margin-top: 48px;
}
.odoc-spec + p {
padding-top: 24px;
diff --git a/example/README.md b/example/README.md
index a1de8a7..2da8e31 100644
--- a/example/README.md
+++ b/example/README.md
@@ -33,10 +33,10 @@ list below and jump to whatever interests you!
prevention.
- [**`e-json`**](e-json#files) — sends and receives JSON
securely.
-- [**`f-static`**](f-static) — serves static files from a
- local directory.
-- [**`g-upload`**](g-upload) — receives file uploads.
-- [**`h-sql`**](h-sql) — finally CRUD!
+- [**`f-static`**](f-static#files) — serves static files from
+ a local directory.
+- [**`g-upload`**](g-upload#files) — receives file uploads.
+- [**`h-sql`**](h-sql#files) — queries an SQL database.
- [**`i-graphql`**](i-graphql)
- [**`j-stream`**](j-stream)
- [**`k-websocket`**](k-websocket)
diff --git a/example/b-session/README.md b/example/b-session/README.md
index 6d8f8bd..06acc13 100644
--- a/example/b-session/README.md
+++ b/example/b-session/README.md
@@ -65,7 +65,7 @@ There are two other session back ends, which are persistent:
[`Dream.run`](https://aantron.github.io/dream/#val-run) so that it doesn't
generate a random key each time.
- [`Dream.sql_sessions`](https://aantron.github.io/dream/#val-sql_sessions)
- stores sessions in a database. You can try it after example
+ stores sessions in a database. It's used in example
[**`h-sql`**](../h-sql#files).
diff --git a/example/h-sql/README.md b/example/h-sql/README.md
new file mode 100644
index 0000000..2e59629
--- /dev/null
+++ b/example/h-sql/README.md
@@ -0,0 +1,163 @@
+# `h-sql`
+
+
+
+Let's serve a list of comments with a comment form!
+
+```ocaml
+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 =
+
+
<%s comment %>
<% ); %> + <%s! Dream.Tag.form ~action:"/" request %> + + + + + +let () = + Dream.run + @@ Dream.logger + @@ Dream.sql_pool "sqlite3:db.sqlite" + @@ Dream.sql_sessions + @@ Dream.router [ + + Dream.get "/" (fun request -> + let%lwt comments = Dream.sql list_comments request in + Dream.respond (render comments request)); + + Dream.post "/" (fun request -> + match%lwt Dream.form request with + | `Ok ["text", text] -> + let%lwt () = Dream.sql (add_comment text) request in + let%lwt comments = Dream.sql list_comments request in + Dream.respond (render comments request) + | _ -> + Dream.empty `Bad_Request); + + ] + @@ Dream.not_found +``` + +$ dune exec --root . ./sql.exe
+
+(executable
+ (name sql)
+ (libraries caqti-driver-sqlite3 dream)
+ (preprocess (pps lwt_ppx)))
+
+(rule
+ (targets sql.ml)
+ (deps sql.eml.ml)
+ (action (run dream_eml %{deps} --workspace %{workspace_root})))
+
+
+
+
+<%s comment %>
<% ); %> + <%s! Dream.Tag.form ~action:"/" request %> + + + + + +let () = + Dream.run + @@ Dream.logger + @@ Dream.sql_pool "sqlite3:db.sqlite" + @@ Dream.sql_sessions + @@ Dream.router [ + + Dream.get "/" (fun request -> + let%lwt comments = Dream.sql list_comments request in + Dream.respond (render comments request)); + + Dream.post "/" (fun request -> + match%lwt Dream.form request with + | `Ok ["text", text] -> + let%lwt () = Dream.sql (add_comment text) request in + let%lwt comments = Dream.sql list_comments request in + Dream.respond (render comments request) + | _ -> + Dream.empty `Bad_Request); + + ] + @@ Dream.not_found diff --git a/src/dream.mli b/src/dream.mli index af1030d..df5040b 100644 --- a/src/dream.mli +++ b/src/dream.mli @@ -706,7 +706,7 @@ val origin_referer_check : middleware - their value must match [Host:] Responds with [400 Bad Request] if the check fails. See example - {{:https://github.com/aantron/dream/tree/master/example/e-json#files} + {{:https://github.com/aantron/dream/tree/master/example/e-json#security} [e-json]}. Implements the @@ -1408,9 +1408,12 @@ val graphiql : string -> handler Dream provides thin convenience functions over {{:https://github.com/paurkedal/ocaml-caqti/#readme} Caqti}, an SQL - interface with several back ends. Dream installs the core - {{:https://opam.ocaml.org/packages/caqti/} [caqti]} package, but you should - also install at least one of: + interface with several back ends. See example + {{:https://github.com/aantron/dream/tree/master/example/h-sql#files} + [h-sql]}. + + Dream installs the core {{:https://opam.ocaml.org/packages/caqti/} [caqti]} + package, but you should also install at least one of: - {{:https://opam.ocaml.org/packages/caqti-driver-sqlite3/} [caqti-driver-sqlite3]} @@ -1435,7 +1438,9 @@ val sql_pool : ?size:int -> string -> middleware (* TODO Work out the example. *) val sql : (Caqti_lwt.connection -> 'a promise) -> request -> 'a promise -(** Runs the callback with a connection from the SQL pool. +(** Runs the callback with a connection from the SQL pool. See example + {{:https://github.com/aantron/dream/tree/master/example/h-sql#files} + [h-sql]}. {[ let () =