1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
|
# OCaml SSR React APP on Eio
This app is a WIP to implement a blog as a React webapp using Ocaml, mlx for JSX, Piaf for HTTP handling, Caqti to handle database queries, using Eio across the app for async.
## Current Status
The project is successfully running as a server-side rendered React application in OCaml with the following features implemented:
- **Database**: SQLite with schema for posts, comments, and votes
- **Routing**: Type-safe routing using the `routes` library
- **SSR React**: JSX-like syntax via mlx, server-side rendering
- **API Endpoints**: RESTful endpoints for posts, comments, and votes
- **Frontend**: Tailwind CSS styling with responsive design
- **Static Assets**: Serving CSS, images, and other static files
## Build the app
To compile the app and see if the code is correct do:
`dune clean && dune build`
## Running the app
```bash
# Build and run the server
dune exec bs5
# Or use the run script
./run.sh
```
## Tech Stack
### Core
- **OCaml**: Primary language
- **Eio**: Async runtime for OCaml 5
- **mlx**: JSX syntax for OCaml React components
- **dune**: Build system
### Web Framework
- **Piaf**: HTTP server and client
- **React (server-side)**: Rendering React components on the server
### Database
- **Caqti**: Database abstraction layer
- **SQLite**: Embedded database with WAL mode
- **Rapper**: Type-safe SQL queries with PPX
### Styling
- **Tailwind CSS**: Utility-first CSS framework
- **Custom fonts**: Crimson Text and Inter fonts included
### Development
- **Nix**: Development environment with flake.nix
- **ocaml-lsp-server**: Language server support
- **ocamlformat-mlx**: Code formatting for mlx files
## Project Structure
```
├── lib/ # Core application code
│ ├── handler.ml # HTTP handlers
│ ├── router.ml # Route definitions
│ ├── html.ml # HTML rendering utilities
│ ├── post_handlers.ml # POST request handlers
│ ├── shared/
│ │ └── query.ml # Database queries
│ └── pages/
│ ├── BlogIndex.mlx # Main blog page React component
│ └── components/ # Reusable React components
├── assets/ # Static assets
│ ├── styles.css # Generated Tailwind CSS
│ ├── fonts/ # Web fonts
│ ├── board/ # Board/category icons
│ └── avatars/ # User avatars
├── bulkdata/ # Database files
│ ├── blog.db # SQLite database
│ └── schema.sql # Database schema
└── bin/ # Main executable
```
## Key Features Implemented
### Database Schema
- **Posts**: id, title, content, date, tags, url
- **Comments**: id, content, date, tags, url, post_id, parent, author
- **Votes**: post_id, comment_id, user_id, vote_type
### API Endpoints
- `GET /` - Main blog page with post previews
- `GET /posts` - Get all posts
- `GET /posts/:id` - Get specific post
- `GET /posts/:id/comments` - Get comments for a post
- `POST /posts` - Create new post
- `POST /comments` - Create new comment
- `POST /votes` - Create new vote
- `GET /assets/*` - Serve static assets
### React Components
- **Layout**: Main page layout with HTML boilerplate
- **PostPreviews**: Displays list of blog post previews
- **Navbar**: Navigation component
- **SiteTitle**: Header component
## Things to take into account
### Ocaml Routes library
#### The `@-->` Operator
The `@-->` operator is from the `routes` library and is used to bind route patterns to handler functions.
##### Example:
```ocaml
`GET, (s "posts" /? nil) @--> Handler.get_posts
```
##### Breaking it down:
- `s "posts"` - matches the string "posts" in the URL path
- `/?` - path concatenation operator
- `nil` - end of path (no more segments)
- `@-->` - "maps to" operator that binds the route to the handler
So `(s "posts" /? nil) @--> Handler.get_posts` means "the route `/posts` maps to the `Handler.get_posts` function".
##### Other common operators in the routes library:
- `/:` for path parameters (e.g., `s "user" /: int /? nil` matches `/user/123`)
- `//` for wildcard paths
- `<$>` for transforming matched values
It's a DSL (domain-specific language) for expressing routes in a concise, type-safe way.
|