v0.5.7 beta
Back-end UI Framework

for feature-rich, secure, and fast web apps in Go

Tutorial

Serve the First Page

0. HTTPS self-signed certs

HTTP without SSL forces HTTP/1.1, which limits the number of parallel requests to a maximum of six.

It’s recommended (but not necessary) to use HTTPS in the development environment.

Generate Certs

In tutorial project dir:

# install package
$ go install filippo.io/mkcert@latest

# makes generated certs trustable by the system (removes browser warning for you), optional 
$ mkcert -install

# create certs in the current folder
$ mkcert localhost 127.0.0.1 ::1
...
The certificate is at "./localhost+2.pem" and the key at "./localhost+2-key.pem" 

1. General Page Template

First, let’s create a baseline template.

./common/page.templ

package common

import "github.com/doors-dev/doors"

// all our pages must be of this shape
type Page interface {
	// method returns component for head insertion
	Head() templ.Component
	// method returns component for body insertion
	Body() templ.Component
}

// now page template that takes `Page` as arg
templ Template(p Page) {
	<!DOCTYPE html>
	<html lang="en">
		<head>
			<meta charset="utf-8"/>
			<meta name="viewport" content="width=device-width, initial-scale=1"/>
			<meta name="color-scheme" content="light dark"/>
			// IMPORTANT: include framework script
			@doors.Include()
			// render page head
			@p.Head()
		</head>
		<body>
			// render page content
			@p.Body()
		</body>
	</html>
}

2. Path Model

Refer to Path Model in the docs for an explanation of how it works.

Now we need to create a path model structure. Let’s put it in a common package so it can be imported into all other packages.

./common/path.go

package common

type HomePath struct {
	Main bool `path:"/"` // match root
}

3. Home Page

Create a new package that will include everything related to the homepage.

Page Template and Page Request Handler

./home/page.templ

package home

import "github.com/doors-dev/doors"

import "github.com/derstruct/doors-tutorial/common"

// alias path for ease of use
type Path = common.HomePath

type homePage struct{}

// implement common.Page interface
templ (h *homePage) Head() {
	<title>hello doors</title>
}

templ (h *homePage) Body() {
	<h1>Yeah!</h1>
}

// Render method is neccessary for page declaration.
// doors.SourceBeam is used to subscribe to path changes 
// (in case of home page there are no parameters or path variants, so we won't use it)
templ (p *homePage) Render(_ doors.SourceBeam[Path]) {
	// use our template
	@common.Template(p)
}

// we also need a handler function to serve our home page when the path matches Path
// pr provides routing options, r - http request wrapper
func Handler(p doors.PageRouter[Path], r doors.RPage[Path]) doors.PageRoute {
	// just serve our home page
	return p.Page(&homePage{})
}

Before proceeding, generate a .go files from .templ

$ templ generate

3. Router

Now, let’s create doors router, attach our page handler, and serve.

./main.go

package main

import (
	"github.com/derstruct/doors-tutorial/home"
	"github.com/doors-dev/doors"
	"net/http"
)

func main() {
	// create doors router
	r := doors.NewRouter()

	// attach home handler
	r.Use(doors.UsePage(home.Handler))

	// start server with our self signed cert
	err := http.ListenAndServeTLS(":8443", "localhost+2.pem", "localhost+2-key.pem", r)
	if err != nil {
		panic(err)
	}
}

The file structure you should have by this point:

.
├── common
│   ├── page_templ.go // autogenerated
│   ├── page.templ
│   └── path.go
├── go.mod
├── go.sum
├── home
│   ├── page.templ
│   ├── page_templ.go // autogenerated
└── main.go
└── localhost+2.pem
└── localhost+2-key.pem

4. Done

$ templ generate && go run .

Then visit https://localhost:8443/ to check; you should see an empty page with a header and title.

If you didn’t install the CA (mkcert -install), you will see a warning first. Just proceed; it’s a self-signed certificate - you can trust it.


Next: Live Reloading