Skip to content

The Server

Every request to your site begins in server/src/Main.gren. If you generated your project with prettynice init, it will look something like this:

module Main exposing (main)
import Gen.Components.Counter
import Node exposing (Environment)
import Prettynice.SimpleRouter as Router
import Prettynice.Request exposing (Request)
import Prettynice.Response as Response exposing (Response)
import Transmutable.Html as H exposing (Html)
import Transmutable.Html.Attributes as A
main : Router.Program
main =
Router.defineProgram
{ init = init
, router = router
}
init : Environment -> Router.Init
init env =
Router.startProgram
{ host = "127.0.0.1"
, port_ = 3000
, env = env
}
router : Request -> Response -> Cmd msg
router request response =
case request.path of
[] ->
response
|> Response.sendHtml
{ title = "My Website"
, head = [ linkStylesheet ]
, body = viewHomePage
}
[ "hello", name ] ->
response
|> Response.sendText ("Hello, " ++ name)
_ ->
response
|> Response.setStatus 404
|> Response.sendText "Not found"
viewHomePage : Array (Html msg)
viewHomePage =
[ H.h1 [] [ H.text "Welcome to my website!" ]
, H.p [] [ H.text "I made this counter just for you." ]
, Gen.Components.Counter.init { start = 0 }
]
linkStylesheet : Html msg
linkStylesheet =
H.link [ A.rel "stylesheet", A.href "/styles.css" ]

If you aren’t familiar with gren, it would be a good idea to read through the book first. The rest of this guide will assume basic familiarity with gren and focus on the parts specific to prettynice.

Defining your Program

We start with a main function that defines a SimpleRouter using defineProgram:

main : Router.Program
main =
Router.defineProgram
{ init = init
, router = router
}

It takes your init and router functions, which we will go over next.

Starting your Program

Your init function should call startProgram. This is where you define the host and port the server will listen on:

init : Environment -> Router.Init
init env =
Router.startProgram
{ host = "127.0.0.1"
, port_ = 3000
, env = env
}

Routing Requests

The heart of your server is the router function. Every request to your site will call this function, with a request holding information about the request, and a response that you can use to respond to the request:

router : Request -> Response -> Cmd msg
router request response =
case request.path of
[] ->
response
|> Response.sendHtml
{ title = "My Website"
, head = [ linkStylesheet ]
, body = viewHomePage
}
[ "hello", name ] ->
response
|> Response.sendText ("Hello, " ++ name)
_ ->
response
|> Response.setStatus 404
|> Response.sendText "Not found"

request.path is an array of strings. It holds the path portion of the URL split on /. For example, requesting 127.0.0.1:3000/bowties/are/cool would have a request.path of ["bowties", "are", "cool"]. Requesting the root path would have an empty array ([]).

You can also match on other things, like the HTTP method:

case request of
{ method = POST, path = "/login" } ->

Take a look at the Request record and see the pages on pattern matching and destructuring in the gren book for help here.

Send HTML

For the homepage, we’re using Response.sendHtml to render HTML:

router : Request -> Response -> Cmd msg
router request response =
case request.path of
[] ->
response
|> Response.sendHtml
{ title = "My Website"
, head = [ linkStylesheet ]
, body = viewHomePage
}
...

Because prettynice needs some control over the <head> and <body> tags, you have some fields to inject what you need into the page:

  • title: Sets the <title> tag. Here we’re rendering <title>My Website</title>
  • head: An array of HTML that will be injected into the <head> tag. Here we’re deferring to a linkStylesheet function to link to a stylesheet. That will be covered in the next section.
  • body: An array of HTML to make up the <body> of your page. Here we’re deferring to a viewHomePage function for this.

Assets and Styling

In the linkStylesheet function we’re linking to /styles.css:

linkStylesheet : Html msg
linkStylesheet =
H.link [ A.rel "stylesheet", A.href "/styles.css" ]

styles.css was created by prettynice init and placed in the public/ directory. Any files in public/ will be accessible at the root url path. So you can put other things there like images, fonts, etc. and reference them in your HTML anywhere you need them.

Initialize Components

The viewHomePage function returns the HTML for our home page:

viewHomePage : Array (Html msg)
viewHomePage =
[ H.h1 [] [ H.text "Welcome to my website!" ]
, H.p [] [ H.text "I made this counter just for you." ]
, Gen.Components.Counter.init { start = 0 }
]

We’re referencing a Gen.Components.Counter in this Html. Where did this come from?

prettynice init created a component at client/src/Components/Counter.gren. When you run prettynice build, prettynice looks at all the components in the Components directory and generates a corresponding module at Gen.Components.[ComponentName] that lets you initialize the component server-side. This lets you initialize your client-side components with data from the server. Here we’re hard-coding that data, but it could just as easily have come from a database, server-side HTTP call, etc.

Learn more about prettynice components in the next section.