Getting Started with Fable. Fable 3 and deployment


Photo by Kelvin Ang on Unsplash

Welcome back! This article is the last part of the “Getting started with Fable” series, so if you missed the previous articles please visit:

The idea of this series is to create a fully functional application that can be used as a starting point for any enterprise application. In particular, I’m looking into a full-stack application written in F# which transpiles into JavaScript / React / Redux application on the front-end and Node.js / Express on the back-end. I know that it sounds a bit weird to transpile F# to JavaScript on the back-end instead of creating a .NET application.

In this last article, I want to share the migration experience from Fable v2 to v3, deployment experience, and some thoughts regarding Fable as a technology.

Fable 3

Frankly speaking, there were no major issues during migration from version 2 to version 3 of the Fable library. For me it was a bit strange decision to move from the npm infrastructure to .NET infrastructure, but it doesn’t make a huge difference.

What you need to do is to create a .NET manifest file and install Fable by running these commands:

dotnet new tool-manifest
dotnet tool install fable

The new manifest file will be generated. It should look something like this:

{
"version": 1,
"isRoot": true,
"tools": {
"fable": {
"version": "3.1.14",
"commands": [
"fable"
]
}
}
}
view raw dotnet-tools.json hosted with ❤ by GitHub

After that you can safely remove all fable dependencies from your node dependencies:

yarn remove fable-compiler fable-loader fable-splitter

Now, we need to take care of scripts in our package.json, because they won’t work anymore:

    "postinstall": "dotnet tool restore",
    "build:server": "node server-prepare && dotnet fable src/api -o dist/api",
    "start": "dotnet fable watch src/app.fsproj -o dist/app --run webpack serve",

Let’s go line by line. The first thing I’ve added is the postinstall script. This script will ensure that we’ve restored our .NET dependencies after restoring npm dependencies. The second script is a bit more interesting. As far as now Fable is a part of .NET infrastructure and it should be executed via dotnet runner we are no longer relying on webpack as the main build tool. Yet, we still need to do some preparation tasks, like copying the package.json file to our distribution directory. The file looks pretty simple right now, but of course, it can be extended with any additional tasks:

// server-prepare.js
const fs = require('fs');

fs.copyFileSync('src/api/package.json', 'dist/api/package.json');

The last script is using Fable‘s ability to run any script after the execution of the Fable script is finished. But to make this script working we need to update the webpack configuration. The first thing we need to do is to remove the Fable mentions from it because now Fable build is a separate script. The second change is about switching the code from the source code to Fable‘s build output. Here is the resulting configuration:

const path = require('path');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const StylelintPlugin = require('stylelint-webpack-plugin');
module.exports = {
entry: './dist/app/main.js',
devServer: {
contentBase: './dist/app',
historyApiFallback: true,
host: '0.0.0.0',
port: 4200
},
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpackPlugin({
title: 'Theater',
template: './src/index.html'
}),
new StylelintPlugin({
configFile: 'stylelint.config.js',
context: './src',
files: '**/*.css',
fix: true
})
],
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'dist/app')
},
module: {
rules: [{
test: /\.css$/,
use: [
'style-loader',
'css-loader',
'postcss-loader'
]
}]
}
};
view raw webpack.config.js hosted with ❤ by GitHub

Basically, that’s all the changes you need to do to upgrade a small project from Fable version 2 to version 3.

Hosting

Now let’s talk a bit about hosting the app. For this Proof of Concept, I’ve chosen the Heroku, but it doesn’t really matter.

The first thing you need to do is to create a new Pipeline:

Creating pipeline

Then you need to create 2 new applications inside this pipeline. The nice thing about Heroku is that now it supports the ability to link GitHub repository directly to the application for build and deployment automation.

Now let’s talk a bit about problems. The first problem is that Heroku still doesn’t have native support for the .NET applications. Luckily it supports the Docker containers so we can create one that we need:

FROM mcr.microsoft.com/dotnet/sdk:5.0
RUN curl -sL https://deb.nodesource.com/setup_14.x | bash – \
&& apt-get install -y nodejs \
&& npm install –global yarn
COPY . .
RUN yarn \
&& yarn build:web \
&& yarn build:api
view raw Dockerfile hosted with ❤ by GitHub

The second problem is a bit more complicated and I was not able to find a good solution for it. The problem that Heroku still doesn’t support monorepo development. You can only have one heroku.yml in your repository and it can only have one set of instructions for one application which is a bit problematic for our case. The only working solution I’ve found is to create a branch and change heroku.yml in this branch. But I understand that this approach won’t work for anything other than PoC:

build:
docker:
web: Dockerfile
run:
# for web app
web: npm run start:web
# for api app
# web: npm run start:web
view raw heroku.yml hosted with ❤ by GitHub

After adding these configurations you should be able to deploy both applications and make them work.

Conclusion

As I’ve mentioned earlier this is the last article in the “getting started with Fable” series. I’ve created a small Proof of Concept application using the stack of technologies I was interested in and was able to successfully deploy it to real servers. Unfortunately, I don’t want to continue this project due to a couple of reasons which I want to share with you.

The first important thing is that I was having hard times using Fable in a way a front-end developer wants to write a front-end application. I want to emphasize that it’s not about Fable being a bad technology it’s all about false expectations. I’m a big fan of JavaScript and front-end development and I hate TypeScript. Currently, I’m looking for technology that can replace TypeScript in front-end development (at least partially). In my opinion, TypeScript is a sort of workaround, it’s definitely not a well-designed modern language, yet it’s very flexible and covers most of the JavaScript use cases (and in case it’s not supported you can just leave pure JavaScript as is). That’s why it’s so popular. On the other hand, Fable is more about doing one thing and doing it great. This technology works perfectly with the Elmish architecture, there are plenty of samples and guides. But if you want to use your past front-end experience to build a Fable application you are unlikely to succeed. There is no information about building Redux-like applications using Fable or building Node.js applications using Fable. And again, I’m not saying that this is bad. It’s really strange to expect a person who knows the .NET to transpile .NET code to JavaScript to run it in the Node.js environment. It’s what I want to do, it’s not what someone else expects.

The second thing is a personal one. I had issues with type definition files. Unfortunately, Fable is missing a lot of type definitions. It’s a very common practice in front-end development to find some useful npm package and add it to the application. And if you are using a TypeScript there is a huge chance that this package already has a type definition file available. If not it’s not a big deal, just cast the library to a special type called “any” and you are good to go. It’s not so simple in the Fable world. I know that there is an automated tool for converting the TypeScript type definition files into F#. But I was not able to get it working, most of the time I was getting partially working type definitions with compilation errors and missing members. Taking into account that I’m relatively new to the F# I was not able to quickly fix those issues. Of course, injecting JavaScript directly into Fable code is much harder than in TypeScript.

So at some point, I’ve caught myself breaking through a lot of issues to write even a couple of lines of code. At this point, I’ve decided that the goal is achieved and I want to try something new.

Again, I’m not saying that the Fable technology is bad. It’s really great and if it fits your needs (.NET back-end + React front-end) you should definitely give it a try. It’s just not what I was looking for.


Thanks a lot for reading! I hope you enjoyed it. If you find this material useful, don’t forget to subscribe and share it with your colleagues and friends! Thanks!

One thought on “Getting Started with Fable. Fable 3 and deployment

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s