## Your Second Haskell Web App ### A Yesod Workshop * Michael Snoyman * VP Engineering, FP Complete<br><img alt="FP Complete logo" src="https://tech.fpcomplete.com/images/fp-complete-logo-small.png" style="border:0"> * Berlin Functional Programming Group * c/o COVID-19 * Thursday, April 23, 2020 --- ## Last time, on Functional Conf * Previous talk: first web app with WAI and Warp * We'll do a quick recap of that... * Then explain what Yesod is * This is an **interactive** workshop * Get your keyboards ready! * https://tech.fpcomplete.com/haskell/get-started ``` stack new mysecondapp yesodweb/simple cd mysecondapp stack build . yesod-bin ``` --- ## Who are the players? * Haskell: strongly typed functional programming language * GHC: the compiler * Stack: the build tool, wraps around GHC * WAI: interface between apps and servers * Warp: a Haskell web server * Yesod: a web framework, built on WAI --- ## What is "built on WAI?" * Yesod applications are WAI applications * Can apply all of the WAI middleware on a Yesod app * Can embed another WAI app inside a Yesod app * Can run Yesod app on any WAI handler --- ## What is Yesod? * It's boring! * Standard MVC framework approach * Original goal: what if a web framework had awesomeness of Haskell's type system? * Want to prevent common bugs at compile time * Want to enforce boundaries --- ## Type system can stop bugs? * Yes it can! * Cross-site scripting (XSS) by having an `HTML` data type * Generally: great way to avoid string escaping bugs * Well-formed URLs are guaranteed by route data type * Can still get 404s due to, e.g., missing database ID * In practice, catches many common bugs --- ## Is boilerplate evil? * Yes and no * Yes: when it can lead to bugs * Use codegen to avoid the problem * High level DSL for routes * Generates: * Route datatype * Parser * Renderer * Dispatcher --- ## Isn't this a workshop? * Oh, right, it is * Hold onto your butts * __LIVE CODING__ Fair warning: first time I've ever done live coding in a talk. --- ## Scaffolding * Run `stack new mysecondapp yesodweb/simple` * Multiple scaffoldings, different DB backends * Skipping database entirely here * Run `cd mysecondapp` * Run `stack build . yesod-bin` * Run `stack exec yesod devel` * Open http://localhost:3000 --- ## Homepage * Open up `templates/homepage.hamlet` * Modify some of the text on the page * Watch the recompile happen * Reload the page, voila! --- ## Routing * Open up `config/routes` * Subsites: embed functionality * We're using static file serving * Create `static/foo.txt` * `curl -i http://localhost:3000/static/foo.txt` * Another common one: authentication * `/` is called `HomeR`, supports `GET` and `POST` * `/comments` is called `CommentR`, supports `POST` --- ## Our first route * Say hi! * Add `/greet GreetR GET` * Recompile fails * Add `import Handler.Greet` in `Application` * Create `Handler.Greet`, copy stuff from `Handler.Home` * Force recompile with enter ```haskell getGreet :: Handler Html getGreet = undefined ``` --- ## Add some code ```haskell {-# LANGUAGE QuasiQuotes #-} getGreetR :: Handler Html getGreetR = do defaultLayout $ do setTitle "Hello there!" [whamlet|<p>Hello there!</p>|] ``` --- ## Add a link * Open `templates/homepage.hamlet` * Go to `Starting` * Add `<a href="/greet">Greetings!` * That's bad, mkay? * `href=@{GreetR}` --- ## URL parameters * What's your name? * Change route: `/greet/#{Text}` * `homepage.hamlet` breaks * Error messages are difficult, sorry * Change `getGreetR` * Warning about unused variable * Use the variable `#{name}` * What about `/greet/<script>` --- ## Footgun * `preEscapedToHtml` * Add `import Text.Blaze.Html` * Add `blaze-html` dependency * Go to http://localhost:3000/greet/%3Cscript%3Ealert(%22danger%20will%20robinson%22)%3C%2fscript%3E --- ## Serving JSON * `pure $ object ["name" .= name]` * `Value` is a funny type * Can provide multiple representations too ```haskell getGreetR :: Text -> Handler TypedContent getGreetR name = selectRep $ do provideRep $ pure $ object ["name" .= name] provideRep $ defaultLayout $ do setTitle "Hello there!" [whamlet|<p>Hello there #{preEscapedToHtml name}!</p>|] ``` curl -i http://localhost:3000/greet/Michael --- ## More info * Check out the rest of the scaffolding * Check out the book https://www.yesodweb.com/book * What else do you want to see?