Building web applications with Haskell: A beginner's guide
Are you looking for a new programming language to learn that is powerful, functional, and well-suited for web development? Look no further than Haskell! Haskell is a functional programming language that is great for building expressive, high-performance, and maintainable web applications. In this article, we’ll guide you through building a web application in Haskell from scratch.
Why Haskell for web development?
Before diving into Haskell web development, let's discuss why it's an excellent choice for building web applications. Haskell's strong type system makes catching errors early in the development process a breeze. Haskell's syntax is concise and expressive, making complex functionality easy to write and maintain. Haskell is also a great choice for high-performance applications due to its strong typing and lazy evaluation.
Setting up your Haskell development environment
Before we jump into building web applications, we need to set up our development environment. To enable a seamless development process with Haskell, we recommend installing Haskell Stack. Stack is a build tool and a package manager that makes managing dependencies and building Haskell packages and libraries a breeze.
Once we have Haskell Stack installed, we create a new project by running the following command in our terminal:
stack new my-web-app
This command creates a new directory called my-web-app
that contains a simple project template.
Getting familiar with Haskell web frameworks
Now that we have our project set up, we need a web framework to build our application. There are many web frameworks available in Haskell, but for this tutorial, we’ll be working with the Yesod framework. Yesod is a high-level web framework that provides powerful tools for building scalable web applications. It emphasizes type safety and enforces a strict separation of concerns between the web application and the database.
We can add Yesod to our project by adding it to our dependency list in the stack.yaml
file:
resolver: lts-17.14
packages:
- .
- yesod
extra-deps: []
After running stack build
to install the dependencies, we can use the Yesod executable to generate a new project skeleton:
stack exec -- yesod init
This command generates a skeleton application with boilerplate code already implemented, ready for us to start customizing.
Defining the data model of your web application
The key to building a great web application is a well-designed data model. Our web application will store and display a list of articles, so our data model will include an Article
type comprising a title, content, and date.
{-# LANGUAGE DeriveGeneric #-}
module Model where
import Data.Text (Text)
import Data.Time (UTCTime)
import GHC.Generics
data Article = Article
{ articleTitle :: Text
, articleContent :: Text
, articleDate :: UTCTime
}
deriving (Show, Eq, Generic)
In the code above, we've defined the Article
type using the DeriveGeneric
language pragma. This pragma allows us to use a generic programming approach to automatically derive serialization and deserialization functions for our data model.
Defining the routes of your web application
Routes are how users interact with our web application. We define them in a file called config/routes
# config/routes
/ HomeR GET
/articles ArticlesR GET POST
/article/#Text ArticleR GET
In the code above, we’ve defined three routes: HomeR
for our homepage, ArticlesR
to display all articles or accept a new article creation form, and ArticleR
to display a specific article.
Defining the handlers of your web application
Handlers are the functions that control the behavior of your web application for each route. We define them in a file called Handler.hs
.
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}
module Handler where
import Data.Aeson (decode, encode)
import Data.ByteString.Lazy (toStrict)
import Data.Text (Text)
import Data.Time (getCurrentTime)
import Model (Article(..))
import Yesod
data WebApp = WebApp
mkYesod "WebApp" [parseRoutes|
/ HomeR GET
/articles ArticlesR GET POST
/article/#Text ArticleR GET
|]
instance Yesod WebApp
instance YesodPersist WebApp where
type YesodPersistBackend WebApp = SqlBackend
runDB action = do
pool <- getPool
runSqlPool action pool
getHomeR :: Handler Html
getHomeR = defaultLayout $ do
[whamlet|
<h1>Welcome to my web app!</h1>
<a href=@{ArticlesR}>Go to the articles page.
|]
getArticlesR :: Handler TypedContent
getArticlesR = do
articles <- runDB $ selectList [] [Desc ArticleDate]
selectRep $ do
provideRep $ defaultLayout $ do
[whamlet|
<h1>List of articles</h1>
<ul>
$forall Entity _ article <- articles
<li>
<a href=@{ArticleR (articleTitle article)}>#{articleTitle article}
<br>
#{articleDate article}
<a href=@{ArticlesR}>Create a new article.
|]
provideJson articles
postArticlesR :: Handler Value
postArticlesR = do
articleJson <- requireJsonBody :: Handler Value
let decodeArticle = decode $ encode articleJson :: Maybe Article
case decodeArticle of
Just article -> do
date <- liftIO getCurrentTime
_ <- runDB $ insert $ article { articleDate = date }
returnJson article
Nothing -> invalidArgs ["Invalid parameters."]
getArticleR :: Text -> Handler TypedContent
getArticleR title = do
maybeArticle <- runDB $ selectFirst [ArticleTitle ==. title] []
case maybeArticle of
Just (Entity _ article) -> selectRep $ do
provideRep $ defaultLayout $ do
[whamlet|
<h1>#{articleTitle article}</h1>
<p>#{articleContent article}</p>
|]
provideJson article
Nothing -> notFound
getPool :: Handler (Pool SqlBackend)
getPool = do
app <- getYesod
return $ appConnPool app
In the code above, we define our WebApp
type and its Yesod instance. We also define the handlers for each of our routes, including getHomeR
, which renders the homepage, getArticlesR
, which queries the database and returns a list of all articles, postArticlesR
, which inserts new articles into the database, and getArticleR
, which queries the database and displays a specific article.
Creating the database schema
To interact with our PostgreSQL database, we'll use the persistent-postgresql
package to define a Schema.hs
file:
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE TypeFamilies #-}
module Schema where
import Data.Text (Text)
import Data.Time (UTCTime)
import Database.Persist
import Database.Persist.Postgresql
import GHC.Generics
import Model
share [mkPersist sqlSettings, mkMigrate "migrateAll"] [persistLowerCase|
ArticleDb
title Text
content Text
date UTCTime default=now()
deriving Generic Show Eq
|]
fromModel :: Article -> ArticleDb
fromModel (Article title content date) =
ArticleDb title content date
toModel :: ArticleDb -> Article
toModel (ArticleDb title content date) =
Article title content date
Our Schema.hs
file defines a single entity named ArticleDb
, with fields corresponding to the fields in our Article
type. Additionally, we define two functions for converting between our Haskell types and our database types.
Running the web application
With our data model, routes, handlers, and database schema defined, we can start our web application using stack exec -- yesod devel
. With our application running, we can visit http://localhost:3000/ to see our homepage.
We can also visit http://localhost:3000/articles to see a list of articles.
When we click on the title of an article, we navigate to the page for that article.
Conclusion
In this article, we’ve shown you how to build a web application with Haskell and Yesod from scratch. We’ve covered defining the data model, routes, handlers, and database schema, and run the application using the Yesod executable.
With Haskell’s powerful type system and high-level web frameworks like Yesod, building expressive, scalable, and maintainable web applications has never been easier. We hope this beginner’s guide will help you get started building your own web applications with Haskell.
Editor Recommended Sites
AI and Tech NewsBest Online AI Courses
Classic Writing Analysis
Tears of the Kingdom Roleplay
GSLM: Generative spoken language model, Generative Spoken Language Model getting started guides
Ocaml Tips: Ocaml Programming Tips and tricks
Developer Key Takeaways: Dev lessons learned and best practice from todays top conference videos, courses and books
Haskell Programming: Learn haskell programming language. Best practice and getting started guides
Video Game Speedrun: Youtube videos of the most popular games being speed run