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>)