Web development 101

Henning Koch
Head of Development at makandra
@triskweline

Web applications?

Native apps vs. web apps

Native applications

Entire app runs on a single computer

Entire app is (often) written in a single language
Popular languages: Java, C#, C++, Objective-C

Web applications

Application runs on multiple computers
Typical: One server, many clients (= web browsers)

Client and server run different parts of the application
and they communicate over HTTP

Application is written in a "stack" of many technologies
Typical stack: HTML, CSS, JavaScript, Ruby/Node/Java/C#, SQL

Language Purpose Runs where?
HTML Textual content Client
CSS Layout & Design Client
JavaScript Client-side interaction Client
HTTP Client/server communication Client/server
Ruby/Node/Java/C# Controller & Model Server
SQL Database access Server

In 2022 web apps are the dominant form of user-facing applications

  • Can be used from any device with a web browser
    (Windows, Linux, macOS, Android, iOS, ...)
  • No installation
    Web browser downloads and "installs" a fresh copy every time you visit a website
  • Is always the latest version
  • Inherently multi-user
  • Gets better every year (vs. native apps)
    File access, offline, audio processing, 3D graphics, ...

Today

  • HTML & HTTP
  • CSS
  • Break
  • Server-side web apps
  • JavaScript
  • Client-side web apps (SPAs)
  • Discussion / end


Read along

http://webdev101.makandra.de
https://github.com/makandra/webdev101

HTML
Hypertext Markup Language

HTTP
Hypertext Transfer Protocol

HTML

<html>
  <body>
    <h1>Hi there!</h1>
    <p>Look at this image:</p>
    <img src="image.jpg">
  </body>
</html>

View example
(use the inspector)

HTTP

User enters into her browser's address bar:

http://makandra.com/page.html

What happens?

Browser asks server:

GET /page.html HTTP/1.1        # I want the file /page.html
Host: makandra.com             # I want it from makandra.com
Accept: text/html, text/plain  # I understand HTML and plain text

Server looks for a local file page.html and replies:

HTTP/1.1 200 OK                # I found what you wanted
Content-Length: 114            # What you want has 114 bytes
Content-Type: text/html        # And it's in HTML format
                               # Here it comes:
<html>
  <body>
    <h1>Hi there!</h1>
    <p>Look at this image:</p>
    <img src="image.jpg">
  </body>
</html>

The browser parses the HTML and encounters this tag:

<img src="image.jpg">

What happens?

Browser asks server:

GET /image.jpg HTTP/1.1        # I want the file /image.jpg
Host: makandra.com             # I want it from makandra.com
Accept: image/jpeg, image/png  # I understand JPEG and PNG

Server looks for a local file image.jpg and replies:

HTTP/1.1 200 OK                # I found what you wanted
Content-Length: 67840          # What you want has 66 KB
Content-Type: image/jpeg       # And it's in JPEG format
                               # Here it comes:
FF D8 FF E0 00 10 4A 46
49 46 00 01 01 01 00 48
00 48 00 00 FF E1 1C 13
45 78 69 66 00 00 49 49
2A 00 08 00 00 00 0D 00
0F 01 02 00 06 00 00 00
...

HTML

CSS
Cascading Style Sheets

HTML

<html>
  <body>
    <h1>About Capybaras</h1>
    <p>
      The capybara is the
      largest rodent
      in the world.
    </p>
  </body>
</html>
 
 

Browser

HTML

<html>
  <body>
    <h1>About Capybaras</h1>
    <p>
      The capybara is the
      largest rodent
      in the world.
    </p>
  </body>
</html>
 
 

CSS

body {
  font-family: 'Arial';
}

h1 {
  text-transform: uppercase;
  font-style: italic;
}

b {
  background-color: #ff9900;
}

View both together

Colors in CSS

#ff0000
#00ff00
#0000ff
#ffff00
#ffffff
#000000
#ff9900
#4488bb
#9900ff
#777777
#22aa88
#885555
red
yellow
fuchsia
rebeccapurple
rgba(255, 0, 0, 1.0)
rgba(255, 0, 0, 0.75)
rgba(255, 0, 0, 0.5)
rgba(255, 0, 0, 0.25)
hsl(0, 100%, 50%)
hsl(30, 100%, 50%)
hsl(30, 50%, 50%)
hsl(30, 50%, 70%)

How to link HTML and CSS (1)

<html>
  <head>
    <style>
      h1 {
        color: #ff0000;
      }
    </style>
  </head>
  <body>
    ...
  </body>
</html>

How to link HTML and CSS (2)

<html>
  <head>
    <link rel="stylesheet" href="styles.css">




  </head>
  <body>
    ...
  </body>
</html>

The browser parses the HTML and encounters this tag:

<link rel="stylesheet" href="styles.css">

What happens?

Browser asks server:

GET /styles.css HTTP/1.1       # I want the file /styles.css
Host: makandra.com             # I know you as makandra.com
Accept: text/css               # I understand the CSS format

Server looks for a local file styles.css and replies:

HTTP/1.1 200 OK                # I found what you wanted
Content-Length: 182            # What you want has 182 bytes
Content-Type: text/css         # And it's in CSS format
                               # Here it comes:
body {
  font-family: 'Arial';
}

h1 {
  text-transform: uppercase;
  font-style: italic;
}

...

An advanced CSS example

View example

Selectors

body {
  font-family: 'Arial';
}

h1 {
  text-transform: uppercase;
  font-style: italic;
}

b {
  background-color: #ff0000;
}

Selectors

body {
  font-family: 'Arial';
}

h1 {
  text-transform: uppercase;
  font-style: italic;
}

b {
  background-color: #ff0000;
}

 


<h1>About Capybaras</h1>

<p class="introduction">
  The capybara is the largest <b>rodent</b>
  in the world.
</p>

<p>
  The capybara belongs to the subfamily
  <b>Hydrochoerinae</b> along with ...
</p>

h1 { ... }


<h1>About Capybaras</h1>

<p class="introduction">
  The capybara is the largest <b>rodent</b>
  in the world.
</p>

<p>
  The capybara belongs to the subfamily
  <b>Hydrochoerinae</b> along with ...
</p>

p { ... }


<h1>About Capybaras</h1>

<p class="introduction">
  The capybara is the largest <b>rodent</b>
  in the world.
</p>

<p>
  The capybara belongs to the subfamily
  <b>Hydrochoerinae</b> along with ...
</p>

p.introduction { ... }


<h1>About Capybaras</h1>

<p class="introduction">
  The capybara is the largest <b>rodent</b>
  in the world.
</p>

<p>
  The capybara belongs to the subfamily
  <b>Hydrochoerinae</b> along with ...
</p>

p:last-child { ... }


<h1>About Capybaras</h1>

<p class="introduction">
  The capybara is the largest <b>rodent</b>
  in the world.
</p>

<p>
  The capybara belongs to the subfamily
  <b>Hydrochoerinae</b> along with ...
</p>

h1, p:last-child { ... }


<h1>About Capybaras</h1>

<p class="introduction">
  The capybara is the largest <b>rodent</b>
  in the world.
</p>

<p>
  The capybara belongs to the subfamily
  <b>Hydrochoerinae</b> along with ...
</p>

h1+p { ... }


<h1>About Capybaras</h1>

<p class="introduction">
  The capybara is the largest <b>rodent</b>
  in the world.
</p>

<p>
  The capybara belongs to the subfamily
  <b>Hydrochoerinae</b> along with ...
</p>

b { ... }


<h1>About Capybaras</h1>

<p class="introduction">
  The capybara is the largest <b>rodent</b>
  in the world.
</p>

<p>
  The capybara belongs to the subfamily
  <b>Hydrochoerinae</b> along with ...
</p>

p.introduction b { ... }


<h1>About Capybaras</h1>

<p class="introduction">
  The capybara is the largest <b>rodent</b>
  in the world.
</p>

<p>
  The capybara belongs to the subfamily
  <b>Hydrochoerinae</b> along with ...
</p>
(Time check)

The box model

View example

Putting elements side-by-side
With Flexbox

View example

CSS frameworks

Instead of starting from scratch you can build upon someone else's CSS.
CSS frameworks like Bootstrap or Foundation give you:

  • Normalized base styles
  • Typography
  • Grid system
  • Form styles
  • Components (cards, modals, tabs, etc.)
  • Utility classes

View example

More CSS resources and exercises

After the break

  • Server-side web apps
  • JavaScript
  • Client-side web apps

Server-side
Web applications

Let's write a web app that returns the current time:

http://timeapp.com/now.html

Browser asks server:

GET /now.html HTTP/1.1         # I want the file /now.html
Host: timeapp.com              # I want it from timeapp.com
Accept: text/html, text/plain  # I understand HTML and plain text

Server looks for a local file now.html and replies:

HTTP/1.1 200 OK                # I found what you wanted
Content-Length: 22             # What you want has 22 bytes
Content-Type: text/plain       # And it's in plain text
                               # Here it comes:
The time is 15:00:00.

 
 
 

Browser asks server:

GET /now.html HTTP/1.1         # I want the file /now.html
Host: timeapp.com              # I want it from timeapp.com
Accept: text/html, text/plain  # I understand HTML and plain text

Server runs a program time.exe.
The program time.exe writes the current time to STDOUT:

print "The time is " + Time.now.to_s

Server takes the program output and sends it the browser:

HTTP/1.1 200 OK                # I found what you wanted
Content-Length: 22             # What you want has 22 bytes
Content-Type: text/plain       # And it's in plain text
                               # Here it comes:
The time is 14:37:05.

Browser don't know about web apps!

The browser simply makes a request and gets back a string of HTML.

The browser doesn't even know if the HTML was generated by an app, or was simply read from a static file.

You can write web apps in any language that can print text

Popular choices:

  • Node.js
  • Ruby
  • Java
  • C#
  • PHP
  • Python

The following examples are in Ruby, but concepts apply to all languages.

Ruby

class User

  def initialize(name, age)
    @name = name
    @age = age
  end

  def adult?
    @age >= 18
  end

end

user = User.new('Max', 18)
puts user.name

Java

class User {

  private String name;
  private int age;

  public User(name, age) {
    this.name = name;
    this.age = age;
  }

  public boolean isAdult() {
    return age >= 18;
  }

}

var user = new User("Max", 18);
System.out.println(user.getName());

The chat app

To run the chat app:

cd examples/2000_chat/src
ruby app.rb

You can now access the app at http://localhost:4567/

The user types into her address bar:

http://chatapp.com/

What happens?

Browser asks server:

GET / HTTP/1.1                 # I want /
Host: chatapp.com              # I want it from chatapp.com
Accept: text/html, text/plain  # I understand HTML and plain text

Server asks the program app.rb for /.

The program prints a new HTML page with the latest chat messages.

The server takes the program output and sends it the browser:

HTTP/1.1 200 OK                # I found what you wanted
Content-Length: 1589           # What you want has 1589 bytes
Content-Type: text/html        # And it's in HTML format
                               # Here it comes:
<html>
  ...
</html>

The user sends a message to the chat channel:

Hello world

What happens?

Browser asks server:

POST /send HTTP/1.1                              # I'm posting data to /send
Host: chatapp.com                                # I'm posting it to chatapp.com
Content-Type: application/x-www-form-urlencoded  # I'm posting key/value pairs
                                                 # Here it comes:
message=Hello+world

Server calls the program app.rb with the posted data.

The program stores the message and prints a HTTP response.

The server takes the program output and sends it the browser:

HTTP/1.1 303 See Other   # This conversation continues elsewhere
Location: /              # Request / to continue

The browser automatically makes another request to / and gets a fresh copy of the message list.

Improving the
chat interface

  • When another user sends us a message,
    we don't notice until we reload the page
  • The message list should automatically scroll to the most recent message
  • It would be nice to be able to search through our messages

These things require code that runs in the browser, not on the server.

JavaScript

// This is a comment

let x = 1
let y = x + 1

console.log(y) // 2
function square(n) {
  return n * n
}

let x = square(16)
console.log(x)    // 256

let z = square
console.log(z)    // function { ... }
console.log(z(4)) // 16

HTML

one

two

three

Browser API

let div = document.querySelector('div')
let children = div.children
console.log(children) // [

one

,

two

,

three

] let parent = children[0].parentNode console.log(parent) // [
...
] console.log(parent === div) // true children[1].style.color = 'red'

View example

How to link HTML and JavaScript (1)

<html>
  <body>

    

Hi world

<script> var x = 1; var y = x + y; </script> </body> </html>

How to link HTML and JavaScript (2)

<html>
  <body>

    

Hi world

<script src="scripts.js"></script> </body> </html>

Events

HTML

<html>
  <body>
    
  </body>
</html>

JavaScript

let button = document.querySelector('button')
button.addEventListener('click', function(event) {
  alert('Button was clicked!')
})

window.addEventListener(function(event) {
  alert('User scrolled!')
})

JavaScript is not my favorite language

  • Not everything is an object.
  • There are three kinds of null.
  • Equality is confusing. Also you can't define equality for your own types.
  • Objects don't crash when accessing missing properties.
  • Explicit this.foo() to call your own methods.
  • Arcane rules around this.
  • Weak standard library (strings, numbers, arrays...).
  • People are still arguing how one JavaScript file can load another.

Improving the
chat interface (1)

Scrolling to the bottom

function showLastMessage() {
  let lastMessage = document.querySelector('.message:last-child')
  if (lastMessage) {
    lastMessage.scrollIntoView()
  }
}

window.addEventListener('load', showLastMessage)

Improving the
chat interface (2)

How do we notice when another user sends a message?
We don't

Classic web applications
are a slide show

⇢ User requests a website
⇠ Server sends a fresh copy

⇢ User clicks a link
⇠ Server sends a fresh copy

⇢ User submits a form
⇠ Server sends a fresh copy

⇢ User reloads the page manually
⇠ Server sends a fresh copy

⇣ Another user sends a message
× Server has no way to push the update to the user

Refreshing with AJAX

async function refresh() {
  let response = await fetch('/')
  let newHTML = await response.text()
  let parser = new DOMParser()
  let newDocument = parser.parseFromString(newHTML, 'text/html')
  let newChannel = newDocument.querySelector('.channel')
  let oldChannel = document.querySelector('.channel')
  oldChannel.replaceWith(newChannel)
}

setInterval(refresh, 1000)

Improving the
chat interface (3)

Client-side search

function search() {
  let queryInput = document.querySelector('.search-form input')
  let query = queryInput.value.toLowerCase()
  let messageElements = document.querySelectorAll('.message')
  for (let messageElement of messageElements) {
    var text = messageElement.textContent.toLowerCase()
    if (text.includes(query)) {
      messageElement.style.display = 'block'
    } else {
      messageElement.style.display = 'none'
    }
  }
}

window.addEventListener('load', function() {
  let queryInput = document.querySelector('.search-form input')
  queryInput.addEventListener('input', search)
})

More JavaScript resources and exercises

Improving app.rb

Our current implementation has app.rb is a single script
with a lot of responsibilities:

  • Routing
  • Storage
  • Business logic

As software engineers we always try to separate responsibilities into individual modules.

Version 1

get '/' do
  @messages = File.read('messages.txt').lines
  erb(:channel)
end

get '/send' do
  message = params['message'] + "\n"
  File.write('messages.txt', message, mode: 'a')
  redirect('/')
end
 
 
 
 
 
 

SQL database

db = Mydb::Client.new(host: 'localhost')

get '/' do
  @messages = db.select_values('SELECT text FROM messages')
  erb(:channel)
end

get '/send' do
  message = params['message']
  escaped = db.escape(message)
  db.execute('INSERT INTO messages VALUES (' + escaped + ')')
  redirect('/')
end
 
 
 

Controller

channel = Channel.new

get '/' do
  @messages = channel.messages
  erb(:channel)
end

get '/send' do
  message = params['message']
  channel.add_message(message)
  redirect('/')
end
 
 
 

Model

class Channel

  def initialize
    @db = Mydb::Client.new
  end

  def messages
    @db.select_values('SELECT text FROM messages')
  end

  def add_message(message)
    escaped = @db.escape(message)
    @db.execute('INSERT INTO messages VALUES (' + escaped + ')')
  end

end

Layer cake

Layer Responsibility
Database Data storage & queries
Model O/R mapping, business logic
Controller Layer orchestration
View Content
Stylesheets Optics (optional)
JavaScript Behavior enhancements (optional)

Client-side web apps

Also known as Single-page apps or SPAs.

  • HTML is rendered on the browser.
  • Client-side state management.
  • Server is reduced to a JSON API.
  • Optimistic rendering.

View source

Pick your trade-off

Server-side app Client-side app
Code complexity low high
Language your choice JavaScript
Data access sync async via API
Initial load time fast slow
Optimistic rendering duplicates logic view bound to client-side state
Offline very hard hard

There are also hybrid solutions like Unpoly, htmx or Remix.

Questions?

Get in touch

@triskweline
henning.koch@makandra.de

Slides

http://webdev101.makandra.de
https://github.com/makandra/webdev101

telnet example.com 80

Browser compatibility

Your new friend: caniuse.com

Scaling the chat app