sihl

A web framework for Reason and OCaml
IN THIS PACKAGE

Sihl

Sihl is a framework for building applications with OCaml and Reason.

Check out the tutorial to get started.

Overview

Following section outlines the design goals, features and main ingredients of Sihl.

Design goals

Following five design goals guide Sihl's development.

Developer happiness

The overarching goal is to maximize developer happiness. We think it is crucial to like the tools that we use.

Sustainable speed of development

This goal is about minimizing the gap between early speed of development and long-term maintanability. Combine functional programming, strict static typing and a modular architecture to quickly build systems that you are not afraid to touch. Thanks to the fast compiler, incremental build time is measured in milliseconds. Run your tests often and quickly. Omit type declarations while prototyping and let the compiler infer the types for you. Once the APIs settle, the compiler infers interfaces.

Composability

Apart from a tiny core that deals with dependency injection, everything is built using services and plain functions, even built-in in features. Compose your apps by picking functionality, one by one.

Minimization of magic

Many frameworks rely on complex macros at compile-time or reflection at runtime to offer ergonomic APIs. Sihl tries to minimize this sort of magic by making things explicit.

Sane defaults, powerful customizations

Sihl provides consistent and sane defaults while allowing for customizations. Get started with your project and fine-tune your BCrypt rounds and database connection pool sizes later.

Features

Here is a list of some of topics that Sihl is concerned with:

  • Data: Connection pooling, migrations, database interaction
  • Authentication: JWT, session management, token management
  • Web: HTTP, middlewares, cookies, routes, templates, flash messages, JSON API
  • Testing: Unit Tests, service tests, End-to-End tests, seeding, Fixtures
  • Custom command line commands
  • Pagination, sorting & filtering
  • Logging
  • Email scheduling, bulk email sending
  • User management
  • Authorization
  • Job Queue
  • Scheduler
  • Block Storage
  • Caching
  • PDF, XLSX & CSV Export
  • Secrets management
  • Admin UI

The main ingredients

The "framework" is not only the code in the GitHub repo, but also documenation, concepts and architecture. Sihl consists of four parts.

Set of libraries

The OCaml ecosystem has many wonderful libraries that are often wrappers around battle tested C libraries. Sihl provides a consistent API on top with sane defaults for web development. Examples are: base64 encoded random byte generation, JWT encoding/decoding, BCrypt hashing, UUID generation.

Documenation & Best Practices

This document describes an architecture following principles of Domain-Driven Design. You are free to use any other architecture and to pick the parts of Sihl that you need.

Interfaces and default implementations

Everything is built using services, even built-in functionality. Sihl provides a set of service interfaces and default implementations for common tasks in web development like email sending, job queues, block storage and migrations. Implementing those interfaces is one of the main ways to extend Sihl.

Tiny core

To be 100% honest, there is a tiny core that deals with service dependency injection and life cycle management. But that's it, everything else is using that mechanism to provide its functionality! // TODO link to source of tiny core as a proof

Installation

opam

Install opam according to this.

Sihl has not been released to OPAM yet, we plan to release it in the coming days.

Run opaminstallsihl.

TODO Provide starter skeleton as git repo

esy

TODO

nix

TODO

Tutorial

TODO

1. Your first Sihl app

  • introduce folder structure
  • separating http/cli from actual application

2. Your first Test

3. Your first Use Case

  • create test for use case
  • create use case, use first
  • authz
  • in memory repo

4. Your first JSON API

5. Your first Website

  • add web stuff: site and json endpoint

5. Your first CLI Command

  • add cli interface

6. Your first Migration

  • implement postgresql repo

7. Launch your app 🚀

  • production settings
  • build dockerfile

Concepts

On order to navigate the code base of provided Sihl modules and to make most of this documentation, there are some concepts that you need to familiarize yourself with.

Option, Result and Lwt

Make sure you understand conceptually Option, Result and Lwt. You should also familiarize yourself with the basic API of those modules.

Option

TODO vs. null/absence of something

Result

TODO no exceptions, just result

Lwt

Sihl uses Lwt to do non-blocking I/O. Because we assume that any I/O might fail, almost every API has nested Result.t inside Lwt.t like so ('a, 'e) Result.t Lwt.t. This is in fact so common that Lwt provides a helper module Lwt_result.t to ergonomically handle the nested Result.t.

Since OCaml 4.08.1, the language has built in support for let* syntax. This is similar to async/await in other languages like JavaScript. Sihl makes extensive use of this new syntax to flatten Lwt chains. This is a good primer.

This allows us to express domain logic without obfuscating it with noise that is caused by asynchronous operations.

Common patterns

TODO show common patterns:

  • Result.of_option(~error)
  • Lwt_result.map_err(...)

Architecture

TODO show file system

Context

Use Case

Service

Repository

Model

Job

Web & Command Line

Extending Sihl

Middleware

Service

Web

Routing

Middleware

Session

Message

Authentication

Query

Template

CSRF

Error Handling

JSON

JSON Web Token

Command Line

Service

Container

Context

Atomicity

Repository

Migration

Query

Meta Data

Testing

Unit Testing

Use Case & Service Testing

End-to-End Testing

Authorization

Use Guards

Define Guards

Email

Session

Admin UI

Block Storage

Job Queue

Scheduler

Cache

XLSX & CSV

PDF

Secret

Signals

Recipes

Add pagination and sorting to a list of things

Add filtering to a list of things

Schedule PDF exports

End-to-End testing "password reset workflow"

Share moddel business logic with a ReasonReact app

Create a middleware that measures the request/response time

Sihl Development