HORIZON HASKELLDocslts/ghc-9.10.x248f8f02026-10-05Search names, modules, packages, or :: a typeCtrl K

GHC 9.10.3 · lts/ghc-9.10.x · 248f8f0 · 2026-10-05

Moduleservant-static-th-1.0.0.0Haskell2010

Servant.Static.TH

This module provides the createApiAndServerDecs function. At compile time, it will read all the files under a specified directory, embed their contents, create a Servant "API" type synonym representing their directory layout, and create a ServerT function for serving their contents statically.

Let's assume that we have a directory called "dir" in the root of our Haskell web API that looks like this:

  $ tree dir/
  dir/
  ├── js
  │   └── test.js
  ├── hello.html
  └── index.html

Here's the contents of "hello.html", "index.html", and "js/test.js":

  $ cat dir/hello.html
  <p>Hello World</p>
  $ cat dir/index.html
  <p>This is the index</p>
  $ cat dir/js/test.js
  console.log("hello world");

The createApiAndServerDecs function can be used like the following:

  {-# LANGUAGE DataKinds #-}
  {-# LANGUAGE TemplateHaskell #-}

  import Data.Proxy (Proxy(Proxy))
  import Network.Wai (Application)
  import Network.Wai.Handler.Warp (run)
  import Servant.Server (serve)
  import Servant.Static.TH (createApiAndServerDecs)

  $(createApiAndServerDecs "FrontEndApi" "frontEndServer" "dir")

  app :: Application
  app = serve (Proxy :: Proxy FrontEndApi) frontEndServer

  main :: IO ()
  main = run 8080 app

createApiAndServerDecs will expand to something like the following at compile time:

  type FrontEndAPI =
    -- index.html is served on the root, as well as from the path "/index.html".
         Servant.API.Get '[HTML] Html
    :<|> "index.html" Servant.API.:> Servant.API.Get '[HTML] Html
    -- hello.html is served from the path "/hello.html".
    :<|> "hello.html" Servant.API.:> Servant.API.Get '[HTML] Html
    -- jstest.js is served from the path "js/test.js".
    :<|> "js" Servant.API.:> "test.js" Servant.API.:> Servant.API.Get '[JS] ByteString

  frontEndServer :: Applicative m => Servant.Server.ServerT FrontEndAPI m
  frontEndServer =
         pure "<p>This is the index</p>"
    :<|> pure "<p>This is the index</p>"
    :<|> pure "<p>Hello World</p>"
    :<|> pure "console.log(\"hello world\");"

If this WAI application is running, it is possible to use curl to access the server:

  $ curl localhost:8080/
  <p>This is the index</p>
  $ curl localhost:8080/index.html
  <p>This is the index</p>
  $ curl localhost:8080/hello.html
  <p>Hello World</p>
  $ curl localhost:8080/js/test.js
  console.log("hello world");

This createApiAndServerDecs function is convenient to use when you want to make a Servant application easy to deploy. All the static frontend files are bundled into the Haskell binary at compile-time, so all you need to do is deploy the Haskell binary. This works well for low-traffic websites like prototypes and internal applications.

This shouldn't be used for high-traffic websites. Instead, you should serve your static files from something like Apache, nginx, or a CDN.

Note:

If you are creating a cabal package that needs to work with cabal-install, the "dir" you want to serve needs to be a relative path inside your project root, and all contained files need to be listed in your .cabal-file under the extra-source-files field so that they are included and available at compile-time.

  • 17 types
  • 13 values

Create API

2 declarations
valuecreateApiType
  1. :: FilePath

    directory name to read files from

  2. -> Q Type
#

Take a template directory argument as a FilePath and create a Servant type representing the files in the directory. Empty directories will be ignored. index.html files will also be served at the root.

For example, assume the following directory structure:

  $ tree dir/
  dir/
  ├── js
  │   └── test.js
  └── index.html

createApiType is used like the following:

  {-# LANGUAGE DataKinds #-}
  {-# LANGUAGE TemplateHaskell #-}

  type FrontEndAPI = $(createApiType "dir")

At compile time, it will expand to the following:

  type FrontEndAPI =
         "js" :> "test.js" :> Get '[JS] ByteString
    :<|> Get '[HTML] Html
    :<|> "index.html" :> Get '[HTML] Html
valuecreateApiDec
  1. :: String

    name of the api type synonym

  2. -> FilePath

    directory name to read files from

  3. -> Q [Dec]
#

This is similar to createApiType, but it creates the whole type synonym declaration.

Given the following code:

  {-# LANGUAGE DataKinds #-}
  {-# LANGUAGE TemplateHaskell #-}

  $(createApiDec "FrontAPI" "dir")

You can think of it as expanding to the following:

  type FrontAPI = $(createApiType "dir")

Create Server

2 declarations
valuecreateServerExp :: FilePath -> Q Exp
#

Take a template directory argument as a FilePath and create a ServerT function that serves the files under the directory. Empty directories will be ignored. index.html files will also be served at the root.

Note that the file contents will be embedded in the function. They will not be served dynamically at runtime. This makes it easy to create a Haskell binary for a website with all static files completely baked-in.

For example, assume the following directory structure and file contents:

  $ tree dir/
  dir/
  ├── js
  │   └── test.js
  └── index.html
  $ cat dir/index.html
  <p>Hello World</p>
  $ cat dir/js/test.js
  console.log("hello world");

createServerExp is used like the following:

  {-# LANGUAGE DataKinds #-}
  {-# LANGUAGE TemplateHaskell #-}

  type FrontEndAPI = $(Servant.Static.TH.Internal.API.createApiType "dir")

  frontEndServer :: Applicative m => ServerT FrontEndAPI m
  frontEndServer = $(createServerExp "dir")

At compile time, this expands to something like the following. This has been slightly simplified to make it easier to understand:

  type FrontEndAPI =
         "js" Servant.API.:> "test.js" Servant.API.:> Servant.API.Get '[JS] ByteString
    :<|> Servant.API.Get '[HTML] Html
    :<|> "index.html" Servant.API.:> Servant.API.Get '[HTML] Html

  frontEndServer :: Applicative m => ServerT FrontEndAPI m
  frontEndServer =
         pure "console.log(\"hello world\");"
    :<|> pure "<p>Hello World</p>"
valuecreateServerDec
  1. :: String

    name of the api type synonym

  2. -> String

    name of the server function

  3. -> FilePath

    directory name to read files from

  4. -> Q [Dec]
#

This is similar to createServerExp, but it creates the whole function declaration.

Given the following code:

  {-# LANGUAGE DataKinds #-}
  {-# LANGUAGE TemplateHaskell #-}

  $(createServerDec "FrontAPI" "frontServer" "dir")

You can think of it as expanding to the following:

  frontServer :: Applicative m => ServerT FrontAPI m
  frontServer = $(createServerExp "dir")

Create Both API and Server

1 declaration
valuecreateApiAndServerDecs
  1. :: String

    name of the api type synonym

  2. -> String

    name of the server function

  3. -> FilePath

    directory name to read files from

  4. -> Q [Dec]
#

This is a combination of createApiDec and createServerDec. This function is the one most users should use.

Given the following code:

  {-# LANGUAGE DataKinds #-}
  {-# LANGUAGE TemplateHaskell #-}

  $(createApiAndServerDecs "FrontAPI" "frontServer" "dir")

You can think of it as expanding to the following:

  $(createApiDec "FrontAPI" "dir")

  $(createServerDec "FrontAPI" "frontServer" "dir")

MIME Types

17 declarations

The following types are the MIME types supported by servant-static-th. If you need additional MIME types supported, feel free to create an issue or PR.

datadata CSS
#
Instances2Accept, MimeRender
  • Accept CSSDefined in servant-static-th-1.0.0.0 · Servant.Static.TH.Internal.Mime
    text/css
  • MimeRender CSS ByteStringDefined in servant-static-th-1.0.0.0 · Servant.Static.TH.Internal.Mime
datadata EOT
#
Instances2Accept, MimeRender
  • Accept EOTDefined in servant-static-th-1.0.0.0 · Servant.Static.TH.Internal.Mime
    fonts/eot
  • MimeRender EOT ByteStringDefined in servant-static-th-1.0.0.0 · Servant.Static.TH.Internal.Mime
datadata GEXF
#

GEXF file (xml for graph application)

Instances2Accept, MimeRender
  • Accept GEXFDefined in servant-static-th-1.0.0.0 · Servant.Static.TH.Internal.Mime
    application/gexf
  • MimeRender GEXF ByteStringDefined in servant-static-th-1.0.0.0 · Servant.Static.TH.Internal.Mime
datadata GIF
#
Instances2Accept, MimeRender
  • Accept GIFDefined in servant-static-th-1.0.0.0 · Servant.Static.TH.Internal.Mime
    image/gif
  • MimeRender GIF ByteStringDefined in servant-static-th-1.0.0.0 · Servant.Static.TH.Internal.Mime
datadata HTML
#
Instances2Accept, MimeRender
  • Accept HTMLDefined in servant-blaze-0.9.1 · Servant.HTML.Blaze
    text/html;charset=utf-8
  • ToMarkup a => MimeRender HTML aDefined in servant-blaze-0.9.1 · Servant.HTML.Blaze
datadata ICO
#
Instances2Accept, MimeRender
  • Accept ICODefined in servant-static-th-1.0.0.0 · Servant.Static.TH.Internal.Mime
    icon/ico
  • MimeRender ICO ByteStringDefined in servant-static-th-1.0.0.0 · Servant.Static.TH.Internal.Mime
datadata JPEG
#
Instances2Accept, MimeRender
  • Accept JPEGDefined in servant-static-th-1.0.0.0 · Servant.Static.TH.Internal.Mime
    image/jpeg
  • MimeRender JPEG ByteStringDefined in servant-static-th-1.0.0.0 · Servant.Static.TH.Internal.Mime
datadata JS
#
Instances2Accept, MimeRender
  • Accept JSDefined in servant-static-th-1.0.0.0 · Servant.Static.TH.Internal.Mime
    application/javascript
  • MimeRender JS ByteStringDefined in servant-static-th-1.0.0.0 · Servant.Static.TH.Internal.Mime
datadata JSON
#

JSON file

Instances2Accept, MimeRender
  • Accept JSONDefined in servant-static-th-1.0.0.0 · Servant.Static.TH.Internal.Mime
    application/json
  • MimeRender JSON ByteStringDefined in servant-static-th-1.0.0.0 · Servant.Static.TH.Internal.Mime
datadata PNG
#
Instances2Accept, MimeRender
  • Accept PNGDefined in servant-static-th-1.0.0.0 · Servant.Static.TH.Internal.Mime
    image/png
  • MimeRender PNG ByteStringDefined in servant-static-th-1.0.0.0 · Servant.Static.TH.Internal.Mime
datadata SVG
#
Instances2Accept, MimeRender
  • Accept SVGDefined in servant-static-th-1.0.0.0 · Servant.Static.TH.Internal.Mime
    image/svg
  • MimeRender SVG ByteStringDefined in servant-static-th-1.0.0.0 · Servant.Static.TH.Internal.Mime
datadata TTF
#
Instances2Accept, MimeRender
  • Accept TTFDefined in servant-static-th-1.0.0.0 · Servant.Static.TH.Internal.Mime
    fonts/ttf
  • MimeRender TTF ByteStringDefined in servant-static-th-1.0.0.0 · Servant.Static.TH.Internal.Mime
datadata TXT
#
Instances2Accept, MimeRender
  • Accept TXTDefined in servant-static-th-1.0.0.0 · Servant.Static.TH.Internal.Mime
    text/plain
  • MimeRender TXT ByteStringDefined in servant-static-th-1.0.0.0 · Servant.Static.TH.Internal.Mime
datadata WOFF
#
Instances2Accept, MimeRender
  • Accept WOFFDefined in servant-static-th-1.0.0.0 · Servant.Static.TH.Internal.Mime
    fonts/woff
  • MimeRender WOFF ByteStringDefined in servant-static-th-1.0.0.0 · Servant.Static.TH.Internal.Mime
datadata WOFF2
#
Instances2Accept, MimeRender
  • Accept WOFF2Defined in servant-static-th-1.0.0.0 · Servant.Static.TH.Internal.Mime
    fonts/woff2
  • MimeRender WOFF2 ByteStringDefined in servant-static-th-1.0.0.0 · Servant.Static.TH.Internal.Mime
datadata XML
#

XML file

Instances2Accept, MimeRender
  • Accept XMLDefined in servant-static-th-1.0.0.0 · Servant.Static.TH.Internal.Mime
    application/xml
  • MimeRender XML ByteStringDefined in servant-static-th-1.0.0.0 · Servant.Static.TH.Internal.Mime

Easy-To-Use Names and Paths

0 declarations

The functions in this section pick defaults for the template directory, api name, and the server function name. This makes it easy to use for quick-and-dirty code.

Paths and Names

API

Server

Server and API