Building a Web Application with Haskell
Are you tired of using the same programming languages to build your web applications? Are you looking for a language that can help you build high-performance applications without sacrificing readability or maintainability? Look no further than Haskell!
Haskell is a functional programming language that is gaining popularity in the web development community due to its ability to create efficient, expressive and type-safe code. In this article, we will walk you through the process of building a web application with Haskell, from development environment setup to deployment.
Set Up a Development Environment
Before we start building our web application, we need to set up our development environment. We will be using Stack, a build tool for Haskell, to manage dependencies and sandbox our projects.
$ curl -sSL https://get.haskellstack.org/ | sh
Once we have Stack installed, we can use it to create a new project.
$ stack new my-app
This command will create a new directory called my-app
with a basic project structure.
Create a Simple Web Server
Let's start by creating a simple web server. We will be using the wai
and warp
packages to create our server.
Add the following packages to your my-app.cabal
file.
...
executable my-app
main-is: Main.hs
other-modules: Lib
hs-source-dirs: src
build-depends: base >= 4.7 && < 5
, wai
, warp
default-language: Haskell2010
...
In src/Main.hs
, add the following code.
{-# LANGUAGE OverloadedStrings #-}
module Main where
import Network.Wai
import Network.HTTP.Types
import Network.Wai.Handler.Warp (run)
app :: Application
app _ respond = respond $ responseLBS
status200
[("Content-Type", "text/plain")]
"Hello, world!"
main :: IO ()
main = run 8080 app
We have defined a simple Application
that responds to every request with a "Hello, world!" message. We then start our server on port 8080
using run
.
Add Database Support
Now that we have our server up and running, let's add database support to our application. We will be using the postgresql-simple
package to interact with our PostgreSQL database.
Add the following package to your my-app.cabal
file.
...
executable my-app
main-is: Main.hs
other-modules: Lib
hs-source-dirs: src
build-depends: base >= 4.7 && < 5
, wai
, warp
, postgresql-simple
default-language: Haskell2010
...
In src/Lib.hs
, add the following code.
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE DeriveGeneric #-}
module Lib where
import GHC.Generics
import Data.Aeson
import Network.Wai
import Network.HTTP.Types
import Network.Wai.Handler.Warp (run)
import Database.PostgreSQL.Simple
data User = User { userId :: Int, userName :: String }
deriving (Show, Generic)
instance ToJSON User
app :: Connection -> Application
app conn req respond = do
users <- liftIO $ query_ conn "SELECT id, name FROM users"
respond $ responseLBS
status200
[("Content-Type", "application/json")]
$ encode users
createTable :: Connection -> IO ()
createTable conn =
execute_ conn "CREATE TABLE IF NOT EXISTS users (id SERIAL PRIMARY KEY, name VARCHAR(255) NOT NULL);"
addUser :: Connection -> String -> IO Integer
addUser conn name =
execute conn "INSERT INTO users (name) VALUES (?)"
(Only name)
main :: IO ()
main = do
conn <- connectPostgreSQL "postgresql://user:password@localhost/mydb"
createTable conn
run 8080 $ app conn
We have defined a User
data type and made it an instance of the ToJSON
typeclass to allow us to serialize it to JSON. We have modified our app
function to query our users
table and return the results as a JSON response. Finally, we have defined two helper functions createTable
and addUser
to assist with database manipulation.
Our main
function now establishes a connection to our database, creates the users
table if it does not exist, and passes the connection to our app
function.
Deploy to Production
Now that we have a functioning web application with database support, let's deploy it to production. We will be using Docker to containerize our application and PostgreSQL.
Create a new Dockerfile
in the root of our project with the following contents.
FROM fpco/stack-build:lts-16.31 AS builder
RUN mkdir /opt/build
WORKDIR /opt/build
COPY stack.yaml /opt/build/
COPY my-app.cabal /opt/build/
RUN stack setup
RUN stack build --only-dependencies
COPY . /opt/build/
RUN stack install --system-ghc
FROM ubuntu:20.04
RUN apt-get update && apt-get install -y libpq-dev
COPY --from=builder /usr/local/bin/my-app /usr/local/bin/my-app
EXPOSE 8080
CMD ["my-app"]
This Dockerfile uses two stages to build our application. The first stage uses fpco/stack-build
as a base image and installs our dependencies. The second stage uses ubuntu:20.04
as a base image and copies our binary from the previous stage.
We will be using Docker Compose to manage our production deployment. Create a new docker-compose.yml
file with the following contents.
version: '3'
services:
db:
image: postgres:11-alpine
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: password
POSTGRES_DB: mydb
volumes:
- db-data:/var/lib/postgresql/data
app:
build: .
ports:
- "8080:8080"
depends_on:
- db
environment:
PGUSER: "user"
PGPASSWORD: "password"
PGHOST: "db"
PGDATABASE: "mydb"
command: ["my-app"]
volumes:
db-data:
This docker-compose.yml
file defines two services: a PostgreSQL database and our web application. The database service uses the postgres:11-alpine
image and defines environment variables for the POSTGRES_USER
, POSTGRES_PASSWORD
and POSTGRES_DB
. It also mounts a volume to persist the database data.
Our application service builds the image using the Dockerfile defined earlier and exposes port 8080
. It depends on the database service and defines environment variables for database connection information. Finally, it sets the command
to start our web application.
We can now deploy our application to production by running the following command.
$ docker-compose up -d
Our web application is now running in a containerized environment and is ready to handle production traffic.
Conclusion
In this article, we have walked through the process of building a web application with Haskell, from development environment setup to deployment. We have shown how to create a simple web server using the wai
and warp
packages, and how to add database support using postgresql-simple
. Finally, we have demonstrated how to deploy our application to production using Docker and Docker Compose.
Haskell's expressive and type-safe nature makes it an ideal language for building web applications that are both efficient and maintainable. By following the steps outlined in this article, you too can start building high-performance web applications with Haskell.
Editor Recommended Sites
AI and Tech NewsBest Online AI Courses
Classic Writing Analysis
Tears of the Kingdom Roleplay
Learn Cloud SQL: Learn to use cloud SQL tools by AWS and GCP
Best Datawarehouse: Data warehouse best practice across the biggest players, redshift, bigquery, presto, clickhouse
Developer Wish I had known: What I wished I known before I started working on
Machine learning Classifiers: Machine learning Classifiers - Identify Objects, people, gender, age, animals, plant types
Flutter consulting - DFW flutter development & Southlake / Westlake Flutter Engineering: Flutter development agency for dallas Fort worth