
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/">
    <channel>
        <title><![CDATA[ The Cloudflare Blog ]]></title>
        <description><![CDATA[ Get the latest news on how products at Cloudflare are built, technologies used, and join the teams helping to build a better Internet. ]]></description>
        <link>https://blog.cloudflare.com</link>
        <atom:link href="https://blog.cloudflare.com/" rel="self" type="application/rss+xml"/>
        <language>en-us</language>
        <image>
            <url>https://blog.cloudflare.com/favicon.png</url>
            <title>The Cloudflare Blog</title>
            <link>https://blog.cloudflare.com</link>
        </image>
        <lastBuildDate>Fri, 17 Apr 2026 00:51:32 GMT</lastBuildDate>
        <item>
            <title><![CDATA[Let's build a Cloudflare Worker with WebAssembly and Haskell]]></title>
            <link>https://blog.cloudflare.com/cloudflare-worker-with-webassembly-and-haskell/</link>
            <pubDate>Tue, 06 Oct 2020 11:00:00 GMT</pubDate>
            <description><![CDATA[ Let's combine the power of Haskell and WebAssembly in a Cloudflare Worker! ]]></description>
            <content:encoded><![CDATA[ <p><i>This is a guest post by Cristhian Motoche of </i><a href="https://www.stackbuilders.com/"><i>Stack Builders</i></a><i>.</i></p><p>At <a href="https://www.stackbuilders.com/">Stack Builders</a>, we believe that Haskell’s system of expressive static types offers many benefits to the software industry and the world-wide community that depends on our services. In order to fully realize these benefits, it is necessary to have proper training and access to an ecosystem that allows for reliable deployment of services. In exploring the tools that help us run our systems based on Haskell, our developer Cristhian Motoche has created a tutorial that shows how to compile Haskell to WebAssembly using Asterius for deployment on Cloudflare.</p>
    <div>
      <h3></h3>
      <a href="#">
        
      </a>
    </div>
    <p>What is a Cloudflare Worker?</p><p><a href="https://developers.cloudflare.com/workers/">Cloudflare Workers</a> is a serverless platform that allows us to run our code on the edge of the Cloudflare infrastructure. It's built on Google V8, so it’s possible to write functionalities in JavaScript or any other language that targets WebAssembly.</p><p><a href="https://webassembly.org/">WebAssembly</a> is a portable binary instruction format that can be executed fast in a memory-safe sandboxed environment. For this reason, it’s especially useful for tasks that need to perform resource-demanding and self-contained operations.</p>
    <div>
      <h3>Why use Haskell to target WebAssembly?</h3>
      <a href="#why-use-haskell-to-target-webassembly">
        
      </a>
    </div>
    <p>Haskell is a pure functional <a href="https://github.com/appcypher/awesome-wasm-langs">language</a> that can target WebAssembly. As such, It helps developers break down complex tasks into small functions that can later be composed to do complex tasks. Additionally, it’s statically typed and has type inference, so it will complain if there are type errors at compile time. Because of that and <a href="https://wiki.haskell.org/Why_Haskell_matters">much more</a>, Haskell is a good source language for targeting WebAssembly.</p>
    <div>
      <h3>From Haskell to WebAssembly</h3>
      <a href="#from-haskell-to-webassembly">
        
      </a>
    </div>
    <p>We’ll use <a href="https://asterius.netlify.app/">Asterius</a> to target WebAssembly from Haskell. It’s a well documented tool that is updated often and supports a lot of Haskell features.</p><p>First, as suggested in the <a href="https://asterius.netlify.app/images.html#using-prebuilt-container-images">documentation</a>, we’ll use podman to pull the Asterius prebuilt container from Docker hub. In this tutorial, we will use Asterius version <a href="https://hub.docker.com/layers/terrorjack/asterius/200617/images/sha256-8e6009198c3940d1b4938b0ab6c0b119d4fce3d7ac0f6336214e758f66887b80?context=explore">200617</a>, which works with GHC 8.8.</p>
            <pre><code>podman run -it --rm -v $(pwd):/workspace -w /workspace terrorjack/asterius:200617</code></pre>
            <p>Now we’ll create a Haskell module called <code>fact.hs</code> file that will export a pure function:</p>
            <pre><code>module Factorial (fact) where

fact :: Int -&gt; Int
fact n = go n 1
  where
    go 0 acc = acc
    go n acc = go (n - 1) (n*acc)

foreign export javascript "fact" fact :: Int -&gt; Int</code></pre>
            <p>In this module, we define a pure function called <code>fact</code>, optimized with tail recursion and exported using the <a href="https://asterius.netlify.app/jsffi.html">Asterius JavaScript FFI</a>, so that it can be called when a WebAssembly module is instantiated in JavaScript.</p><p>Next, we’ll create a JavaScript file called <code>fact_node.mjs</code> that contains the following code:</p>
            <pre><code>import * as rts from "./rts.mjs";
import module from "./fact.wasm.mjs";
import req from "./fact.req.mjs";

async function handleModule(m) {
  const i = await rts.newAsteriusInstance(Object.assign(req, {module: m}));
  const result = await i.exports.fact(5);
  console.log(result);
}

module.then(handleModule);</code></pre>
            <p>This code imports <code>rts.mjs</code> (common runtime), WebAssembly loaders, and the required parameters for the Asterius instance. It creates a new Asterius instance, it calls the exported function <code>fact</code> with the input <code>5</code>, and prints out the result.</p><p>You probably have noted that <code>fact</code> is done <b>asynchronously</b>. This happens with any exported function by Asterius, even if it’s a pure function.</p><p>Next, we’ll compile this code using the Asterius command line interface (CLI) <code>ahc-link</code>, and we’ll run the JavaScript code in Node:</p>
            <pre><code>ahc-link \
  --input-hs fact.hs \
  --no-main \
  --export-function=fact \
  --run \
  --input-mjs fact_node.mjs \
  --output-dir=node</code></pre>
            <p>This command takes <code>fact.hs</code> as a Haskell input file, specifies that no <code>main</code> function is exported, and exports the <code>fact</code> function. Additionally, it takes <code>fact_node.mjs</code> as the entry JavaScript file that replaces the generated file by default, and it places the generated code in a directory called <code>node</code>.</p><p>Running the <code>ahc-link</code> command from above will print the following output in the console:</p>
            <pre><code>[INFO] Compiling fact.hs to WebAssembly
...
[INFO] Running node/fact.mjs
120</code></pre>
            <p>As you can see, the result is executed in <code>node</code> and it prints out the result of <code>fact</code> in the console.</p>
    <div>
      <h3>Push your code to Cloudflare Workers</h3>
      <a href="#push-your-code-to-cloudflare-workers">
        
      </a>
    </div>
    <p>Now we’ll set everything up for deploying our code to Cloudflare Workers.</p><p>First, let’s add a <code>metadata.json</code> file with the following content:</p>
            <pre><code>{
  "body_part": "script",
  "bindings": [
    {
      "type": "wasm_module",
      "name": "WASM",
      "part": "wasm"
    }
  ]
}</code></pre>
            <p>This file is needed to specify the <code>wasm_module</code> binding. The <code>name</code> value corresponds to the global variable to access the WebAssembly module from your Worker code. In our example, it’s going to have the name <code>WASM</code>.</p><p>Our next step is to define the main point of the Workers script.</p>
            <pre><code>import * as rts from "./rts.mjs";
import fact from "./fact.req.mjs";

async function handleFact(param) {
  const i = await rts.newAsteriusInstance(Object.assign(fact, { module: WASM }));
  return await i.exports.fact(param);
}

async function handleRequest(req) {
  if (req.method == "POST") {
    const data = await req.formData();
    const param = parseInt(data.get("param"));
    if (param) {
      const resp = await handleFact(param);
      return new Response(resp, {status: 200});
    } else {
      return new Response(
        "Expecting 'param' in request to be an integer",
        {status: 400},
      );
    }
  }
  return new Response("Method not allowed", {status: 405});
}

addEventListener("fetch", event =&gt; {
  event.respondWith(handleRequest(event.request))
})</code></pre>
            <p>There are a few interesting things to point out in this code:</p><ol><li><p>We import <code>rts.mjs</code> and <code>fact.req.mjs</code> to load the exported functions from our WebAssembly module.</p></li><li><p><code>handleFact</code> is an asynchronous function that creates an instance of Asterius with the global <code>WASM</code> module, as a Workers global variable, and calls the exported function <code>fact</code> with some input.</p></li><li><p><code>handleRequest</code> handles the request of the Worker. It expects a <code>POST</code> request, with a parameter called <code>param</code> in the request body. If <code>param</code> is a number, it calls <code>handleFact</code> to respond with the result of <code>fact</code>.</p></li><li><p>Using the Service Workers API, we listen to the <code>fetch</code> event that will respond with the result of <code>handleRequest</code>.</p></li></ol><p>We need to build and bundle our code in a single JavaScript file, because Workers only accepts one script per worker. Fortunately, Asterius comes with Parcel.js, which will bundle all the necessary code in a single JavaScript file.</p>
            <pre><code>ahc-link \
  --input-hs fact.hs \
  --no-main \
  --export-function=fact \
  --input-mjs fact_cfw.mjs \
  --bundle \
  --browser \
  --output-dir worker</code></pre>
            <p><code>ahc-link</code> will generate some files inside a directory called <code>worker</code>. For our Workers, we’re only interested in the JavaScript file (<code>fact.js</code>) and the WebAssembly module (<code>fact.wasm</code>). Now, it’s time to submit both of them to Workers. We can do this with the provided REST API.</p><p>Make sure you have an account id (<code>$CF_ACCOUNT_ID</code>), a name for your script (<code>$SCRIPT_NAME</code>), and an API Token (<code>$CF_API_TOKEN</code>):</p>
            <pre><code>cd worker
curl -X PUT "https://api.cloudflare.com/client/v4/accounts/$CF_ACCOUNT_ID/workers/scripts/$SCRIPT_NAME" \
     -H  "Authorization: Bearer $CF_API_TOKEN" \
     -F "metadata=@metadata.json;type=application/json" \
     -F "script=@fact.js;type=application/javascript" \
     -F "wasm=@fact.wasm;type=application/wasm"</code></pre>
            <p>Now, visit the Workers UI, where you can use the editor to view, edit, and test the script. Also, you can enable it to on a <code>workers.dev</code> subdomain (<code>$CFW_SUBDOMAIN</code>); in that case, you could then simply:</p>
            <pre><code>curl -X POST $CFW_SUBDOMAIN \
       -H 'Content-Type: application/x-www-form-urlencoded' \
       --data 'param=5'</code></pre>
            
    <div>
      <h3>Beyond a simple Haskell file</h3>
      <a href="#beyond-a-simple-haskell-file">
        
      </a>
    </div>
    <p>So far, we’ve created a WebAssembly module that exports a pure Haskell function we ran in Workers. However, we can also create and build a Cabal project using Asterius <code>ahc-cabal</code> CLI, and then use <code>ahc-dist</code> to compile it to WebAssembly.</p><p>First, let’s create the project:</p>
            <pre><code>ahc-cabal init -m -p cabal-cfw-example</code></pre>
            <p>Then, let’s add some dependencies to our cabal project. The cabal file will look like this:</p>
            <pre><code>cabal-version:       2.4
name:                cabal-cfw-example
version:             0.1.0.0
license:             NONE

executable cabal-cfw-example
  ghc-options: -optl--export-function=handleReq
  main-is:             Main.hs
  build-depends:
    base,
    bytestring,
    aeson &gt;=1.5 &amp;&amp; &lt; 1.6,
    text
  default-language:    Haskell2010</code></pre>
            <p>It’s a simple cabal file, except for the <code>-optl--export-function=handleReq</code> ghc flag. This is <a href="https://github.com/tweag/asterius/issues/362#issuecomment-561576162">necessary</a> when exporting a function from a cabal project.</p><p>In this example, we’ll define a simple <code>User</code> record, and we’ll define its instance automatically using Template Haskell!</p>
            <pre><code>{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell   #-}

module Main where

import           Asterius.Types
import           Control.Monad
import           Data.Aeson                 hiding (Object)
import qualified Data.Aeson                 as A
import           Data.Aeson.TH
import qualified Data.ByteString.Lazy.Char8 as B8
import           Data.Text


main :: IO ()
main = putStrLn "CFW Cabal"

data User =
  User
    { name :: Text
    , age  :: Int
    }

$(deriveJSON defaultOptions 'User)</code></pre>
            <p><b>NOTE:</b> It’s not necessary to create a Cabal project for this example, because the prebuilt container comes with a lot of <a href="https://github.com/tweag/asterius/issues/354">prebuilt packages</a> (aesona included). Nevertheless, it will help us show the potential of <code>ahc-cabal</code> and <code>ahc-dist</code>.</p><p>Next, we’ll define <code>handleReq</code>, which we’ll export using JavaScript FFI just like we did before.</p>
            <pre><code>handleReq :: JSString -&gt; JSString -&gt; IO JSObject
handleReq method rawBody =
  case fromJSString method of
    "POST" -&gt;
      let eitherUser :: Either String User
          eitherUser = eitherDecode (B8.pack $ fromJSString rawBody)
       in case eitherUser of
            Right _  -&gt; js_new_response (toJSString "Success!") 200
            Left err -&gt; js_new_response (toJSString err) 400
    _ -&gt; js_new_response (toJSString "Not a valid method") 405

foreign export javascript "handleReq" handleReq :: JSString -&gt; JSString -&gt; IO JSObject

foreign import javascript "new Response($1, {\"status\": $2})"
  js_new_response :: JSString -&gt; Int -&gt; IO JSObject</code></pre>
            <p>This time, we define <code>js_new_response</code>, a Haskell function that creates a JavaScript object, to create a <code>Response</code>. <code>handleReq</code> takes two string parameters from JavaScript and it uses them to prepare a response.</p><p>Now let’s build and install the binary in the current directory:</p>
            <pre><code>ahc-cabal new-install --installdir . --overwrite-policy=always</code></pre>
            <p>This will generate a binary for our executable, called <code>cabal-cfw-example</code>. We’re going to use <code>ahc-dist</code> to take that binary and target WebAssembly:</p>
            <pre><code>ahc-dist --input-exe cabal-cfw-example --export-function=handleReq --no-main --input-mjs cabal_cfw_example.mjs --bundle --browser</code></pre>
            <p><code>cabal_cfw_example.mjs</code> contains the following code:</p>
            <pre><code>import * as rts from "./rts.mjs";
import cabal_cfw_example from "./cabal_cfw_example.req.mjs";

async function handleRequest(req) {
  const i = await rts.newAsteriusInstance(Object.assign(cabal_cfw_example, { module: WASM }));
  const body = await req.text();
  return await i.exports.handleReq(req.method, body);
}

addEventListener("fetch", event =&gt; {
  event.respondWith(handleRequest(event.request))
});</code></pre>
            <p>Finally, we can deploy our code to Workers by defining a <code>metadata.json</code> file and uploading the script and the WebAssembly module using Workers API as we did before.</p>
    <div>
      <h3>Caveats</h3>
      <a href="#caveats">
        
      </a>
    </div>
    <p>Workers <a href="https://developers.cloudflare.com/workers/about/limits/#script-size">limits</a> your JavaScript and WebAssembly in file size. Therefore, you need to be careful with any dependencies you add.</p>
    <div>
      <h3>Conclusion</h3>
      <a href="#conclusion">
        
      </a>
    </div>
    <p>Stack Builders builds better software for better living through technologies like expressive static types. We used Asterius to compile Haskell to WebAssembly and deployed it to Cloudflare Workers using the Workers API. Asterius supports a lot of Haskell features (e.g. Template Haskell) and it provides an easy-to-use JavaScript FFI to interact with JavaScript. Additionally, it provides prebuilt containers that contain a lot of Haskell packages, so you can start writing a script right away.</p><p>Following this approach, we can write functional type-safe code in Haskell, target it to WebAssembly, and publish it to Workers, which runs on the edge of the Cloudflare infrastructure.</p><p>For more content check our <a href="https://www.stackbuilders.com/news/page/1">blogs</a> and <a href="https://www.stackbuilders.com/tutorials/">tutorials</a>!</p> ]]></content:encoded>
            <category><![CDATA[Cloudflare Workers]]></category>
            <category><![CDATA[WebAssembly]]></category>
            <category><![CDATA[WASM]]></category>
            <category><![CDATA[Serverless]]></category>
            <guid isPermaLink="false">5AVLtoBd5aIK5zHzElyM9s</guid>
            <dc:creator>Cristhian Motoche</dc:creator>
        </item>
    </channel>
</rss>