package sihl
Install
Dune Dependency
Authors
Maintainers
Sources
sha256=38d5abd1d80aa4772c742a8bd4024afd39e23e2e02304b1cacfddde7dba2f43b
sha512=28f3236a51a501247a12f38441aab70e100936eca94a34d43a5734d1861021ac15dfa5457f87d9d0f87bc1f2fef9cda6d6fa6d11486797dd1b466d6bd85650ce
Description
A web framework for Reason and OCaml to build apps rapidly.
Published: 19 Aug 2020
README
README.md
Sihl
A framework for statically typed functional web development in OCaml and Reason.
Explore the docs »
View Example Project
·
Report Bug
·
Request Feature
Table of Contents
About
You want to skip the "blah blah" and go straight to the code? Get started here.
What Sihl is not
Let's start by clarifying what Sihl is not:
No MVC framework
Sihl will not help you generate models, controllers and views quickly. It doesn't use convention over configuration and instead tries to be as explicit as necessary. We think the speedup of generating MVC and auto-wiring pales in comparison to the long-term maintanability concerns
No microservice framework
Sihl encourages you to build things in a service-oriented way, but it's not a microservice framework that deals with problems of distributed systems. Use your favorite FaaS/PaaS/container orchestrator/micro-service toolkit to deal with that and Sihl for the business logic.
What Sihl is
Now that we have that out of the way, let's have a look what Sihl is.
Sihl is a high-level application framework that can be used for web development. It provides a set of composable building blocks and recipes that allow you to develop (web) apps quickly but sustainably. Statically typed functional programming with OCaml makes web development fun and safe.
Things like database migrations, HTTP routing, user management, sessions, logging, emailing, job queues and schedules are just a few of all the topics Sihl takes care of.
Do we need another web framework?
Yes, because all other frameworks didn't grow here.
On a more serious note, originally we wanted to collect a set of services, libraries, best practices and architecture to quickly and sustainably spin-off our tools and product. An evaluation of languages and tools lead us to build the 5th iteration of what became Sihl with OCaml. We believe OCaml is a phenomenal host, even though its house of web development is small at the moment.
Sihl is built on OCaml because OCaml ...
... runs fast
... compiles really fast
... is portable and works well on Linux
... is strict but not pure
... is fun to use
But the final and most important reason is the module system, which gives Sihl its modularity and strong compile-time guarantees about a project setup. Sihl uses OCaml modules for statically typed dependency injection. If your app compiles, the dependencies are wired up correctly. You can not use what's not there.
Learn more about it in the concepts.
Getting Started
Following are the steps to quickly get started with a minimal running web server.
Prerequisites
Basic understanding of OCaml
Installation of opam
To initialize opam:
opam init
To install dune (the build system):
opam install dune
Installation
opam install sihl
A simple Sihl app
Let's a simple Sihl app, that is a simple web app with a HTTP route.
We are using https://github.com/ocaml/dune to build the project. Create a dune
file that specifies an executable depending on Sihl.
dune:
(executable
(name app)
(libraries
sihl
)
)
A Sihl app requires at least two things: A minimal set of services (also called kernel services) for the app to run and the actual app definition.
Create the services file to statically set up the services and their dependencies that you are going to use in your project.
service.ml:
module Random = Sihl.Utils.Random.Service
module Log = Sihl.Log.Service
module Config = Sihl.Config.Service
module Db = Sihl.Data.Db.Service
module MigrationRepo = Sihl.Data.Migration.Service.Repo.MariaDb
module Cmd = Sihl.Cmd.Service
module Migration = Sihl.Data.Migration.Service.Make (Cmd) (Db) (MigrationRepo)
module WebServer = Sihl.Web.Server.Service.Make (Cmd)
module Schedule = Sihl.Schedule.Service.Make(Log)
The app configuration file glues all the components together. In this example there is not much to glue except for the services we are going to use and two routes.
We want a simple web service without any database (and thus no migrations), so let's just include Service.WebServer
.
app.ml:
let services: (module Sihl.Core.Container.SERVICE) list =
[
(module Service.WebServer);
]
let hello_page =
Sihl.Web.Route.get "/hello" (fun _ ->
Sihl.Web.Res.(html |> set_body "Hello!") |> Lwt.return)
let hello_api =
Sihl.Web.Route.get "/hello" (fun _ ->
Sihl.Web.Res.(json |> set_body {|{"msg":"Hello!"}|}) |> Lwt.return)
let routes =
[ ("/page", [ hello_page ], []); ("/api", [ hello_api ], []) ]
module App = Sihl.App.Make (Service)
let _ =
App.(
empty
|> with_services services
|> with_routes routes
|> run
)
You can build (and watch) this project with
dune build -w
Run the executable to get a list of all available commands:
./_build/default/app.exe
You should see a start
CLI command. This comes from Service.WebServer
which is the only service we registered. Run the command with
./_build/default/app.exe start
and visit http://localhost:3000/site/hello/
or http://localhost:3000/api/hello/
.
Find a simple starter project here similar to our small example.
Concepts
In essence, Sihl is just a tiny core (about 100 lines) that deals with loading services and their dependencies. Every feature is built using services.
Services
A service is a unit that provides some functionality. Most of the time, a service is just a collection of functions that belong together. This would be the equivalent of a class with just static methods in object-oriented programming. However, some services can be started and stopped which gives them a lifecycle. Sihl makes sure that services with lifecycles are started and stopped in the right order.
Sihl provides service interfaces and some service implementations. As an example, Sihl provides default implementation of the user service for user management with support for MariaDB and PostgreSQL.
When you create a Sihl app, you usually start out with your service setup in a file service.ml
. There, you list all services that you are going to use in the project. We can compose large services out of simple and small services using parameterized modules. This service composition is statically checked and you can use it throughout your project.
Sihl has to be made aware of the services you are going to use. That is why the second step of setting of services is done in the app description file.
App
A Sihl app is described in a app.ml
. Here you glue services from service.ml
, your own code and various other components together. It is the main entry point to your application.
Folder structure
Let's have a look at the folder structure of an example project called pizza-shop
.
.
├── service
│ ├── dune
│ ├── service.ml
├── app
│ ├── dune
│ ├── app.ml
├── components
│ ├── pizza-delivery
│ │ ├── model.ml
│ │ ├── service.ml
│ │ ├── repo.ml
│ ├── pizza-order-taking
│ │ ├── model.ml
│ │ ├── service.ml
│ │ ├── repo.ml
│ │ ├── cmd.ml
├── web
│ ├── routes.ml
│ ├── middlewares.ml
├── cli
│ ├── cmd.ml
There is an emphasis on the separation of the business logic from web stuff. Your app is just a set of services, models and repositories and it is not concerned with HTTP, databases, CLIs or other infrastructure topics.
[TODO goe through folders]
Usage
See the open issues for a list of proposed features (and known issues).
Configuration
[TODO]
Web
[TODO]
Route
[TODO]
Middleware
[TODO]
Database
[TODO]
CLI
[TODO]
User
[TODO]
Authorization
[TODO]
Token
[TODO]
Session
[TODO]
Schedule
[TODO]
[TODO]
Delayed Email
[TODO]
Job queue
[TODO]
Polling job queue
[TODO]
Storage
[TODO]
Roadmap
[TODO long-term plan]
Contributing
Contributions are what make the open source community such an amazing place to be learn, inspire, and create. Any contributions you make are greatly appreciated. If you have any questions just contact us.
Fork the Project
Create your Feature Branch (
git checkout -b feature/amazing-feature
)Commit your Changes (
git commit -m 'Add some amazing feature
)Push to the Branch (
git push origin feature/amazing-feature
)Open a Pull Request
License
Copyright (c) 2020 Oxidizing Systems
Distributed under the MIT License. See LICENSE
for more information.
Contact
Oxidizing Systems - @oxidizingsys - hello@oxidizing.io
Project Link: https://github.com/oxidizing/sihl
Acknowledgements
Sihl would not be possible without amazing projects like following:
Dependencies (28)
-
containers
>= "2.8"
-
alcotest
>= "1.2.0"
-
ppx_sexp_conv
>= "0.13.0"
-
ppx_fields_conv
>= "0.13.0"
-
sexplib
>= "0.13.0"
-
letters
>= "0.1.1" & < "0.2.0"
-
uuidm
>= "0.9.7"
-
jwto
>= "0.3.0"
-
safepass
>= "3.0"
-
pcre
>= "7.4.3"
-
fmt
>= "0.8.8"
-
logs
>= "0.7.0"
-
reason
>= "3.0.0"
-
tyxml-jsx
>= "4.3.0"
-
tyxml
>= "4.3.0"
-
caqti-lwt
>= "1.2.0" & < "2.0.0~"
-
caqti
>= "1.2.1" & < "2.0.0~"
-
lwt_ssl
>= "1.1.3"
-
ssl
>= "0.5.9"
-
tls
>= "0.11.1" & < "1.0.0"
-
tsort
= "2.0.0"
-
ppx_deriving_yojson
>= "3.5.2"
-
yojson
>= "1.7.0"
-
opium
>= "0.17.1" & < "0.19.0"
-
base
>= "v0.13.1" & < "v0.16.0"
-
lwt
>= "5.3.0"
-
ocaml
>= "4.08.0"
-
dune
>= "2.4"
Dev Dependencies (8)
-
cohttp-lwt-unix
>= "2.5.1" & with-test
-
alcotest-lwt
>= "1.2.0" & with-test
-
alcotest
>= "1.2.0" & with-test
-
tuareg
dev
-
ocp-indent
dev
-
ocamlformat
dev
-
merlin
dev
-
utop
dev
Used by
None
Conflicts
None