Examples#

Simple query list#

import "bialet" for Response, Util

var users = `SELECT id, name FROM users`.fetch
var title = "Users list"

Response.out(
  <!doctype html>
  <html>
    <head>
      <title>{{ title }}</title>
      <style>
        body { font: 1.5em/2.5 system-ui; text-align: center }
        ul { list-style-type: none }
      </style>
    </head>
    <body>
      <h1>{{ title }}</h1>
      {{ users.count > 0 ?
        <ul>
          {{ users.map{|user| <li>
            <a href="/hello?{{ Util.params({"id": user["id"]}) }}">
              {{ Util.htmlEscape(user["name"]) }}
            </a>
          </li> } }}
        </ul> :
        <p>No users, go to <a href="/hello">hello</a>.</p>
      }}
    </body>
  </html>
)

Hello World#

import "bialet" for Request, Response

// We create a class that that contains the logic and template.
// This is not required by Bialet, but it is a good practice.
// Once the logic and template become too big, you should split it
// in multiple classes.
class App {
  construct new() {
    _title = "🚲 Welcome to Bialet"
  }
  // Fetch the user from the database using plain SQL
  user(id) { `SELECT * FROM users WHERE id = ?`.first(id) }
  // Get the name from the current user if it exists or the default
  name(id) { user(id)["name"] || "World" }
  // Build the HTML
  html(content) {
    return <!doctype html>
    <html>
      <head>
        <title>{{ _title }}</title>
      </head>
      <body>
        <h1>{{ _title }}</h1>
        {{ content }}
      </body>
    </html>
  }
}

// We use the `query()` method to execute SQL statements.
// In this case we create a table named `users` with two columns: `id` and `name`.
// This should be do it with migrations, here it is added to the script to be self-contained.
`CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT)`.query()
// Also, we ensure that we have some data in the table.
`REPLACE INTO users (id, name) VALUES (1, "Alice"), (2, "Bob"), (3, "Charlie")`.query()

// We use the `get()` method to get the `id` parameter from the URL.
var idUrlParam = Request.get("id")
// Create an instance of the `App` class.
var app = App.new()
// Generate the HTML, with the name of the user.
var html = app.html(
  <p>👋 Hello <b>{{ app.name(idUrlParam) }}</b></p>
)
Response.out(html) // Serve the HTML

Count the visitors of your website#

// This line imports the Response class for managing HTTP interactions.
// The import lines are write at the top of the script.
import "bialet" for Response

// We use the `query()` method to execute SQL statements.
// In this case we create a table named `counter` with two columns: `name` and `value`.
// Then we insert a row with the name `visits` and a value of 0, if there is no row
// with the name `visits`.
// This should be do it with migrations, here it is added to the script to be self-contained.
`CREATE TABLE IF NOT EXISTS counter (name TEXT PRIMARY KEY, value INTEGER)`.query
`INSERT OR IGNORE INTO counter (name, value) VALUES ("visits", 0)`.query

// This is the proper start of the script...

// We increment the value of the row with the name `visits` by 1.
`UPDATE counter SET value = value + 1 WHERE name = "visits"`.query

// We use the `val()` method to get the value of the first row in the query result.
var visits = `SELECT value FROM counter WHERE name = "visits"`.val

// We use the `out()` method to send the response to the client.
// The `{{ ... }}` syntax is used to interpolate the value of the `visits` variable.
// Apart from the interpolation, the string is regular HTML.
Response.out(<!doctype html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="style.css">
  </head>
  <body>
    <h1>Total times visited: <strong>{{ visits }}</strong></h1>
    <p>
      ℹī¸ Reload the browser or
      <a href="">click here</a>
      to see the new value.<p>
    <p><a href=".">Back ↩ī¸</a></p>
  </body>
</html>
)

Count the clicks#

// This line imports the Response and Request classes for managing HTTP interactions.
// The import lines are write at the top of the script.
import "bialet" for Response, Request

// We use the `query` method to execute SQL statements.
// In this case we create a table named `counter` with two columns: `name` and `value`.
// Then we insert a row with the name `clicks` and a value of 0, if there is no row
// with the name `clicks`.
// This should be do it with migrations, here it is added to the script to be self-contained.
`CREATE TABLE IF NOT EXISTS counter (name TEXT PRIMARY KEY, value INTEGER)`.query
`INSERT OR IGNORE INTO counter (name, value) VALUES ("clicks", 0)`.query

// We use the `get()` method to get a parameter from the URL.
// When the parameter is `count`, we increment the value of the `clicks` row by 1.
if (Request.get("count")) {
  `UPDATE counter SET value = value + 1 WHERE name = "clicks"`.query
}

// When the parameter is `reset`, we reset the value of the `clicks` row to 0.
if (Request.get("reset")) {
  `UPDATE counter SET value = 0 WHERE name = "clicks"`.query
}

// We use the `val()` method to get the value of the first row in the query result.
var clicks = `SELECT value FROM counter WHERE name = "clicks"`.val

// We use the `out()` method to send the response to the client.
// The `{{ ... }}` syntax is used to interpolate the value of the `clicks` variable.
// Apart from the interpolation, the string is regular HTML.
Response.out(<!doctype html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="style.css">
  </head>
  <body>
    <h1>Total times clicked: <strong>{{ clicks }}</strong></h1>
    <p>
      <a href="clicks?count">Click me! đŸ”ē</a>
      â€ĸ
      <a href="clicks?reset">Reset 🧹</a>
    </p>
    <p><a href=".">Back ↩ī¸</a></p>
  </body>
</html>
)

Form submission#

// This line imports the Response and Request classes for managing HTTP interactions.
// The import lines are write at the top of the script.
import "bialet" for Response, Request

// We use the `query` method to execute SQL statements.
// In this case we create a table named `counter` with two columns: `name` and `value`.
// Then we insert a row with the name `form` and a value of 0, if there is no row
// with the name `form`.
// This should be do it with migrations, here it is added to the script to be self-contained.
`CREATE TABLE IF NOT EXISTS counter (name TEXT PRIMARY KEY, value INTEGER)`.query
`INSERT OR IGNORE INTO counter (name, value) VALUES ("form", 0)`.query

// Set the initial value to 1
var value = 1

// We use the `isPost` property to check if the request method is POST.
if (Request.isPost) {
  // We use the `post()` method to get a parameter from the form.
  value = Request.post("value")
  // We use the `query` method to execute SQL statements.
  // In this case we update the value of the `form` row by the value of the `value`
  // parameter.
  // Note the use of the `?` placeholder for the value of the `value` parameter.
  // All the queries are run as a prepared statement. You can't concatenate query strings.
  `UPDATE counter SET value = value + ? WHERE name = "form"`.query(value)
}

// We use the `val()` method to get the value of the first row in the query result.
var total = `SELECT value FROM counter WHERE name = "form"`.val

// We use the `out()` method to send the response to the client.
// The `{{ ... )` syntax is used to interpolate the value of the `total` variable.
// and the `value` variable.
// Apart from the interpolation, the string is regular HTML.
Response.out(<!doctype html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="style.css">
  </head>
  <body>
    <h1>Total sum: <strong>{{ total }}</strong></h1>
    <form method="POST">
      <input type="text" name="value" value="{{ value }}">
      <button type="submit">Submit</button>
    </form>
    <p><a href=".">Back ↩ī¸</a></p>
  </body>
</html>)

JSON Response#

// This line imports the Response class for managing HTTP interactions.
// The import lines are write at the top of the script.
import "bialet" for Response

// We use the `query()` method to execute SQL statements.
// In this case we create a table named `counter` with two columns: `name` and `value`.
// This should be do it with migrations, here it is added to the script to be self-contained.
`CREATE TABLE IF NOT EXISTS counter (name TEXT PRIMARY KEY, value INTEGER)`.query()

// We use the `fetch()` method to get the query result as an array of objects.
var counters = `SELECT * FROM counter ORDER BY name ASC`.fetch()

// We use the `json()` method to send the query result as a JSON.
// This will add the header `Content-Type: application/json`.
Response.json(counters)

Form Upload#

import "bialet" for Request, Response, Db

if (Request.isPost) {
  // Get the uploaded file
  var uploadedFile = Request.file("form_file_name")
  System.print("File: %(uploadedFile.name)")
  // Make it temporal, it will be deleted soon
  // You can still use it in the rest of the request
  uploadedFile.temp()
  // Return the file to the browser
  return Response.file(uploadedFile.id)
}

var title = "Upload File"
Response.out(
<!doctype html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>{{ title }}</title>
    <style>body{ font: 1.3em system-ui; text-align: center }</style>
  </head>
  <body>
    <h1>{{ title }}</h1>
    <form method="post" enctype="multipart/form-data">
      <input type="file" name="form_file_name">
      <input type="submit" value="{{ title }}">
    </form>
    <p><a href=".">Back ↩ī¸</a></p>
  </body>
</html>
)

File Creation#

import "bialet" for Response, File
import "random" for Random

// Generate an emoji SVG
var random = Random.new()
var emojis = ["🎉", "🎂", "❤ī¸", "🍔", "🍕"]
var svg = <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
  <text y=".9em" font-size="90">{{ random.sample(emojis) }}</text>
</svg>
// Save the svg in a file
var file = File.create("emoji.svg", "image/svg+xml", svg)
// Send the file
Response.file(file.id)

HTTP API Call#

// This line imports the Response class for managing HTTP interactions.
// The import lines are write at the top of the script.
import "bialet" for Response, Http

var users = Http.get('https://dummyjson.com/users?limit=5&select=username,email')['users']
System.print("Users: {{users)")

Response.out(<!doctype html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="style.css">
  </head>
  <body>
    <h1>Show all the users from the JSONPlaceholder API</h1>
    {{ /* The interpolated string can have comments on it */
      users.count > 0 ?
      <table>
        <tr>
          <th>Name</th>
          <th>Email</th>
        </tr>
        {{ /* List all the users */
          users.map{|user| <tr>
            <td>{{user["username"]}}</td>
            <td>{{user["email"]}}</td>
          </tr>
        } }}
      </table>
      : /* If there are no users */
        <p>No user found</p>
      }}
    <p><a href=".">Back ↩ī¸</a></p>
  </body>
</html>)

Password utilities#

// This line imports the Response and Request classes for managing HTTP interactions.
// It also imports the Util class, that includes the helper functions.
// The import lines are write at the top of the script.
import "bialet" for Request, Response, Util

// We use the `post()` method to get a parameter from the POST request.
var password = Request.post('password')
var passwordCheck = Request.post('password-check')
// Set up the hash and verify variables
var encrypted
var verify

// We use the `isPost` property to check if the request method is POST
if (Request.isPost) {
  // Hash the password. The hash is a string that includes the salt.
  // The salt is randomly generated. The same password will have a different salt each time the `hash()` method is called.
  // The hash use SHA-256 to encode the password with the salt.
  encrypted = Util.hash(password)
  // Verify the password against the hash
  verify = Util.verify(passwordCheck, encrypted)
}

// We use the `out()` method to send the response to the client.
// The `{{ ... }}` syntax is used to interpolate the value inputs.
// Apart from the interpolation, the string is regular HTML.
Response.out(<!doctype html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="style.css">
  </head>
  <body>
    <h1>Password utilities</h1>
    {{ Request.isPost && <div>
        <p>Hash:</p>
        <code>{{ encrypted }}</code>
        <p><strong>{{ verify ? 'The passwords are the same' : 'The passwords are different' }}</strong></p>
        <hr>
      </div>
    }}
    <form method="POST">
      <p>
        <label for="password">Password</label>
      </p>
      <p>
        <input type="text" name="password" value="{{password}}" />
      </p>
      <p>
        <label for="password-check">Password to check</label>
      </p>
      <p>
        <input type="text" name="password-check" value="{{passwordCheck}}" />
      </p>
      <p>
        <button>Submit</button>
      </p>
    </form>
    <p><a href=".">Back ↩ī¸</a></p>
  </body>
</html>)

Cross site request forgery#

// This line imports the Response and Request classes for managing HTTP interactions.
// It also imports the Session class, that includes the Cross-Site Request Forgery functions.
// The import lines are write at the top of the script.
import "bialet" for Request, Response, Session

var session = Session.new()
var verify

// We use the `isPost` property to check if the request method is POST
if (Request.isPost) {
  // Verify that the CSRF token is valid
  verify = session.csrfOk
}

// We use the `out()` method to send the response to the client.
// The `%( ... )` syntax is used to interpolate the value of the `clicks` variable.
// The `session.csrf` generates the hidden input field with the CSRF token.
// Apart from the interpolation, the string is regular HTML.
Response.out(<!doctype html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="style.css">
  </head>
  <body>
    <h1>Cross-Site Request Forgery</h1>
    <p>Use different tabs to verify the post is from the last window.</p>
    {{ Request.isPost &&
      <p><strong>{{ verify ? 'The post is valid' : 'The post come from someone else' }}</strong></p>
    }}
    <form method="POST">
      {{ session.csrf }}
      <p>
        <button>Submit</button>
      </p>
    </form>
    <p><a href=".">Back ↩ī¸</a></p>
  </body>
</html>)