Bunny CDN Purge Cache in Haskell IHP
Tags: haskell, bunnycdn, cdn, ihp
Published: 2023-12-17 23:38:45


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8tkyrcwltocb4m2hzwpw.png)


[Bunny CDN](https://bunny.net/?ref=n86usfpw93) is a content delivery network (CDN) service. It's designed to help improve the loading times and performance of websites by distributing content across multiple servers located around the world. Our own site - the one that you are reading right now - is using this kind of CDN as well.While writing or correcting articles we came up with the requirement to purge cached articles as soon as they update.

Bunny.net provides an API that allows developers to integrate its services with their applications or websites. This API can be used for various tasks like purging cached content, managing zones, or retrieving usage statistics.
The General Approach to Purge Cache

To create a Haskell program that purges a given URL using the bunny.net API, you would typically use an HTTP client library for Haskell, like http-conduit or wreq. For this article, I'll use http-conduit as it's quite popular and versatile. With the help of this library we will asynchronously send a request to the Bunny API Endpoint.
Implementing purgeCache

First, you need to install the http-conduit package if you haven't already. You can do this using Cabal or Stack. When speaking about IHP - which we are using - we just need to add the http-conduit dependency to the Haskell Packages of the App.

Next, you will need the API-Key provided for your account by Bunny.net. This you can usually find in your account area:


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p4ahywhcnnru9nawnbpb.png)


Now, let's write a Haskell function to purge a given URL.

```
import Network.HTTP.Simple
import Network.HTTP.Types.Method (methodPost)

purgeCdnCache :: (?context :: ControllerContext) => Text -> IO ()
purgeCdnCache url = do
  let BunnyCdnApiKey mBunnyCdnKey = getAppConfig @Config.BunnyCdnApiKey
  case mBunnyCdnKey of
    Just apiKey -> do
      let url' = TE.encodeUtf8 url
      let queryParams = [("url", Just url')]
      let request = setRequestMethod methodPost
                $ setRequestSecure True
                $ setRequestPort 443
                $ setRequestHost "api.bunny.net"
                $ setRequestPath "/purge"
                $ setRequestQueryString queryParams
                $ setRequestHeader "AccessKey" [TE.encodeUtf8 apiKey]
                $ defaultRequest
      _ <- forkIO $ do
          response <- httpJSON request :: IO (Response ())
          putStrLn $ "Purge request sent. Response status code: " ++ show (getResponseStatusCode response)
      pure ()
    Nothing -> do
      pure ()
```

We can place this code snippet inside the Controller Helper to be able to use the purge function in any Controller of the App.

This function sets up an HTTP POST request to the bunny.net purge endpoint, including the necessary AccessKey headers and Query-Parameters with the according encoding Yoga. We are forking out the request to a new Thread to not block the execution of the Controller Action. The resulting response status code from the bunny.net server is then printed out.

Remember, you'll need to add the necessary imports and probably native SSL Certificate dependencies for your application (cacert).

Also, keep in mind that working with external APIs might require handling various edge cases and error responses for robust production code. This example is a basic demonstration and may need to be adapted for more complex requirements or error handling.

One last detail that needs to be mentioned is the environment variable for our API Key. I have modeled it to be optional since purging is not necessary in all environments of the application (only in production). The way to set up the environment variable is pretty simple and straight forward in the Config.hs file:

```
bunnyApiKey :: Maybe Text <- envOrNothing "BUNNY_CDN_API_KEY"
option (BunnyCdnApiKey bunnyApiKey)
```

And here you have it, a working CDN purge cache function! Happy purging!