chore: import upstream snapshot with attribution
CI / compile_and_lint (push) Failing after 0s
CI / docker_build (linux/amd64, -linux-amd64-duckdb, duckdb) (push) Failing after 0s
CI / docker_build (linux/arm64, -linux-arm64, minimal) (push) Failing after 2s
CI / docker_build (linux/arm64, -linux-arm64-duckdb, duckdb) (push) Failing after 1s
CI / docker_build (linux/amd64, minimal) (push) Failing after 1s
CI / test (, sqlite, sqlite::memory:) (push) Has been skipped
CI / test (mssql, mssql, mssql://root:Password123!@127.0.0.1/sqlpage) (push) Has been skipped
CI / test (mysql, mysql, mysql://root:Password123!@127.0.0.1/sqlpage) (push) Has been skipped
CI / test (oracle, oracle, Driver=Oracle 21 ODBC driver;Dbq=//127.0.0.1:1521/FREEPDB1;Uid=root;Pwd=Password123!) (push) Has been skipped
CI / test (postgres, odbc, Driver=PostgreSQL Unicode;Server=127.0.0.1;Port=5432;Database=sqlpage;UID=root;PWD=Password123!, true) (push) Has been skipped
CI / test (postgres, postgres, postgres://root:Password123!@127.0.0.1/sqlpage) (push) Has been skipped
CI / playwright (push) Has been skipped
CI / docker_build (linux/arm/v7, -linux-arm-v7, minimal) (push) Failing after 0s
CI / hurl_examples (push) Failing after 8s
deploy website / deploy_official_site (push) Failing after 1s
CI / hurl (${{ matrix.example }}) (push) Has been skipped
CI / docker_push (duckdb) (push) Has been cancelled
CI / docker_push (minimal) (push) Has been cancelled
CI / windows_test (push) Has been cancelled

This commit is contained in:
wehub-resource-sync
2026-07-13 12:31:57 +08:00
commit d718c5a372
986 changed files with 74597 additions and 0 deletions
@@ -0,0 +1,56 @@
select 'http_header' as component,
'public, max-age=300, stale-while-revalidate=3600, stale-if-error=86400' as "Cache-Control";
select 'dynamic' as component, properties FROM example WHERE component = 'shell' LIMIT 1;
select 'hero' as component,
'Custom URLs' as title,
'SQLPage lets you customize responses to URLs that don''t match any file, using `404.sql`.' as description_md,
'not_found.jpg' as image;
select 'text' as component, '
# Handling custom URLs
By default, SQLPage serves the file that matches the URL requested by the client.
If your users enter `https://example.com/about`, SQLPage will serve the file `about/index.sql` in your project.
If you create a file named `about.sql`, SQLPage will serve it when the user requests either `https://example.com/about.sql` or `https://example.com/about` (since v0.33, the `.sql` suffix is optional).
But what if you want to handle URLs that don''t match any file in your project ?
For example, what if you have a blog, and you want nice urls like `example.com/blog/my-trip-to-rome`,
but you don''t want to create a file for each blog post ?
By default, SQLPage would return a sad 404 error if you don''t have a file named `blog/my-trip-to-rome/index.sql`
in your project''s root directory.
But you can customize this behavior by creating a file named `404.sql` in your project.
## The 404.sql file
When SQLPage doesn''t find a file that matches the URL requested by the client, it will serve the file `404.sql` if it exists.
Since v0.28, when SQLPage receives a request for a URL like `https://example.com/a/b/c`, it will look for the file `a/b/c/index.sql` in your project,
and if it doesn''t find it, it will then search for, in order:
- `/a/b/404.sql`
- `/a/404.sql`
- `/404.sql`
## Basic routing example
So, you have a `blog_posts` table in your database, with columns `name`, and `content`.
You want to serve the content of the blog post with id `:id` when the user requests `example.com/blog/:id`.
You can do this by creating a `404.sql` file in the `blog` directory of your project:
```sql
-- blog/404.sql
-- Get the id from the URL
set name = substr(sqlpage.path(), 1+length(''/blog/''));
-- Get the blog post from the database
select ''text'' as component,
content as contents_md
from blog_posts
where name = $name;
```
Now, when a user requests `example.com/blog/my-trip-to-rome`, SQLPage will serve the content of the blog post with name `my-trip-to-rome` from the `blog_posts` table.
' as contents_md;
@@ -0,0 +1 @@
select 'redirect' as component, '../documentation.sql' as link;
Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 193 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 210 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 262 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

@@ -0,0 +1,28 @@
select 'shell' as component,
'SQLPage: get started!' as title,
'database' as icon,
'/' as link,
'en-US' as lang,
'Hosted SQLPage: set-up a SQLPage website in three clicks.' as description,
'documentation' as menu_item,
'Poppins' as font;
SELECT 'hero' as component,
'Hosted SQLPage' as title,
'Work In Progress: We are working on a cloud version of SQLPage
that will enable you to effortlessly set up your website online without the need to download any software or configure your own server.' as description,
'https://upload.wikimedia.org/wikipedia/commons/thumb/9/94/Baustelle_H%C3%B6lzla_6066312.jpg/1024px-Baustelle_H%C3%B6lzla_6066312.jpg' as image,
'https://forms.gle/z1qmuCwdNT5Am7gp6' as link,
'Get notified when we are ready' as link_text;
SELECT 'text' as component,
'Try SQLPage online today' as title,
'
If you want to fiddle around with SQLPage without installing anything on your computer, you can still try it out online today.
Repl.it is an online development environment that allows you to run SQLPage in your browser.
Try [the SQLPage repl](https://replit.com/@pimaj62145/SQLPage).
Click *Use template* to create your own editable copy of the demo website, then click *Run* to see the result.
On the left side you can edit the SQLPage code, on the right side you can see the result.
' as contents_md;
@@ -0,0 +1,87 @@
select 'http_header' as component,
'public, max-age=300, stale-while-revalidate=3600, stale-if-error=86400' as "Cache-Control",
'<https://sql-page.com/your-first-sql-website/>; rel="canonical"' as "Link";
set os = COALESCE($os, case
when sqlpage.header('user-agent') like '%windows%' then 'windows'
when sqlpage.header('user-agent') like '%x11; linux%' then 'linux'
when sqlpage.header('user-agent') like '%x11; ubuntu; linux%' then 'linux'
when sqlpage.header('user-agent') like '%x11; debian; linux%' then 'linux'
when sqlpage.header('user-agent') like '%macintosh%' then 'macos'
else 'any'
end);
-- Fetch the page title and header from the database
select 'dynamic' as component, json_patch(json_extract(properties, '$[0]'), json_object(
'title', 'SQL to Website - Tutorial',
'description', 'Convert your SQL database into a website in minutes. In this 5-minute guide, we will create a simple website from scratch, and learn the basics of SQLPage.'
)) as properties
FROM example WHERE component = 'shell' LIMIT 1;
SET req = '{
"url": "https://api.github.com/repos/sqlpage/SQLPage/releases/latest",
"timeout_ms": 200
}';
SET api_results = sqlpage.fetch_with_meta($req);
SET sqlpage_version = COALESCE(json_extract($api_results, '$.body.tag_name'), '');
SELECT 'hero' as component,
'Your first SQL Website' as title,
'[SQLPage](/) is a free tool for building data-driven apps quickly.
Let''s create a simple website with a database from scratch, to learn SQLPage basics.' as description_md,
case $os
when 'linux' then 'get_started_linux.webp'
when 'macos' then 'get_started_macos.webp'
when 'windows' then 'get_started_windows.webp'
else 'get_started.webp'
end as image,
CASE $os
WHEN 'macos' THEN '#download'
WHEN 'windows' THEN 'https://github.com/sqlpage/SQLPage/releases/latest/download/sqlpage-windows.zip'
WHEN 'linux' THEN 'https://github.com/sqlpage/SQLPage/releases/latest/download/sqlpage-linux.tgz'
ELSE 'https://github.com/sqlpage/SQLPage/releases'
END AS link,
CASE $os
WHEN 'macos' THEN CONCAT('Install SQLPage ', $sqlpage_version, ' using Homebrew')
WHEN 'windows' THEN CONCAT('Download SQLPage ', $sqlpage_version, ' for Windows')
WHEN 'linux' THEN CONCAT('Download SQLPage ', $sqlpage_version, ' for Linux')
ELSE CONCAT('Download SQLPage ', $sqlpage_version)
END AS link_text;
SELECT 'alert' as component,
'Afraid of the setup ? Do it the easy way !' as title,
'mood-happy' as icon,
'teal' as color,
'You dont want to install anything on your computer ?
You can use a preconfigured SQLPage hosted on our servers, and get your app online in minutes, without **ever having to configure a server** yourself.' as description_md,
'https://datapage.app' AS link,
'Host your app on our servers' as link_text;
select 'https://editor.datapage.app' as link, 'Try SQLPage from your browser' as title, 'teal' as color;
SELECT 'alert' as component,
'Do you prefer videos ?' as title,
'brand-youtube' as icon,
'purple' as color,
'We made videos to introduce you to SQLPage. You can watch them on YouTube. The videos cover everything from the underlying technology to the philosophy behind SQLPage to the actual steps to create your first website.' as description_md,
'https://www.youtube.com/watch?v=9NJgH_-zXjY' AS link,
'Watch the introduction video' as link_text;
select 'https://www.youtube.com/watch?v=6D5D10v18b0&list=PLTue_qIAHxActQnLn_tHWZUNXziZTeraB' as link, 'Tutorial video series' as title;
select 'text' as component,
sqlpage.read_file_as_text(
printf('your-first-sql-website/tutorial-install-%s.md',
case
when $os = 'windows' then 'windows'
when $os = 'macos' then 'macos'
else 'any'
end
)
) as contents_md,
true as article,
'download' as id;
select 'text' as component,
sqlpage.read_file_as_text('your-first-sql-website/tutorial.md') as contents_md,
true as article,
'tutorial' as id;
@@ -0,0 +1,146 @@
# Understanding SQL Migrations: Your Database, Layer by Layer
Maintaining a structured and evolving database is crucial for web app development. Rarely do we get a schema 100% correct on day one. New insights about the shape of the application are discovered over time, or business needs themselves evolve. In the world of databases, we can evolve schemas using migration files. These files are just more SQL that append or amend layers of development. Think of this process like sedimentary rock layers. Each migration adds a layer, and together, these layers create a complete, functional structure along with a visible trail of historical changes.
## What Makes up a SQL Migration File?
SQL migrations are incremental changes to a database. These changes can include creating tables, adding columns, modifying data types, or even inserting or updating records. Each migration is a distinct script that applies a specific change.
### Use Caution!
Since migration files change the database, they can have unintended consequences if not thought through carefully. For instance, you may accidentally delete a column that is still being used by your application or remove records that are still needed.
> ⚠️ Be thoughtful, double-check your work, and **always back up your data before running a migration**.
### Order Matters
**It's important that migrations are distinct ordered files** as SQLPage uses the sequence of migration files to build the database over time: `0001_initial_setup.sql`, `0002_my_first_change.sql`, `0003_my_next_change`, etc.
### No take-backs!
**Do not make changes to an existing migration file** in production. If a previously implemented migration file is altered, it will confuse SQLPage and cause a crash.
*If you are in early stages of development and are okay with losing data*, you can delete the database and start over with an altered migration file. However, in a production environment, especially once persisted data is involved, this is not an option.
It's like trying to go back in time and change a previous sedimentary layer. That's not how rocks work, and that's not how migrations work.
Append or amend; do not try to change the past.
## Examples
Let's start off easy with a simple database to store user information: `first_name`, `last_name`, `email`, `phone`, and `password_hash`. Our first migration actually creates the `users` table with these columns. That is, we migrate from *nothing* to *having a users table*.
**`sqlpage/migrations/001_create_users.sql`**:
```sql
create table users (
id integer primary key autoincrement,
first_name not null,
last_name not null,
email not null unique,
phone,
rewards_level,
password_hash not null
);
```
In the terminal, we can see the new schema:
```console
sqlite> .schema
CREATE TABLE users (
id integer primary key autoincrement,
first_name not null,
last_name not null,
email not null unique,
phone,
rewards_level,
password_hash not null
);
```
### A Simple Change
Later, we discover we need a `middle_name` column, so we create a new migration file to add this column to the `users` table. Remember, we must ensure the order is written into the filename so SQLPage can apply them in the correct order when building the database.
**`sqlpage/migrations/002_add_middle_name.sql`**:
```sql
alter table users add column middle_name;
```
In the terminal:
```console
sqlite> .schema
CREATE TABLE users (
id integer primary key autoincrement,
first_name not null,
last_name not null,
email not null unique,
phone,
rewards_level integer,
password_hash not null,
middle_name
);
```
### A More Complex Change
But notice here, SQLite has appended the column to the very end. What if we really need that `middle_name` column to be next to the other name columns? Further, what if we realize `rewards_level` should really be an integer and only one between 1 and 20?
We can create a new migration file to make these changes, albeit a bit more complicated.
Because we'll be altering column types and modifying column order, we'll need to *create a temporary table* to hold the data while we drop the original table and recreate it with the new schema.
**`sqlpage/migrations/003_alter_users.sql`**:
```sql
create table users_temp (
id integer primary key autoincrement,
first_name not null,
last_name not null,
middle_name,
email not null unique,
phone,
rewards_level integer check(rewards_level between 1 and 20),
password_hash not null
);
insert into users_temp select id, first_name, last_name, middle_name, email, phone, rewards_level, password_hash from users;
drop table users; -- backups are important!
alter table users_temp rename to users;
```
In the terminal:
```console
sqlite> .schema
CREATE TABLE users (
id integer primary key autoincrement,
first_name not null,
last_name not null,
middle_name,
email not null unique,
phone,
rewards_level integer check(rewards_level between 1 and 20),
password_hash not null
);
```
## Conclusion
SQL migration is our tool for evolving databases over time. By creating distinct, ordered migration files, we can incrementally build and modify our databases without losing data or breaking our application functionality. Just **remember to always back up data before running a migration**, and always be thoughtful about changes.
Since SQLPage runs migrations forward in time, we won't dive into the complexities of rolling back migrations here. Just remember, we can't change the past, only build upon it.
[Rollbacks](https://en.wikipedia.org/wiki/Rollback_(data_management)) are an intriguing topic that you may run into in other frameworks.
## Further Study
To learn more on the migrations topic, consider the Wikipedia article on [Schema Migration](https://en.wikipedia.org/wiki/Schema_migration). **Note**: database engines are different, so be sure to review the documentation for your specific database engine and what types of SQL statements are permitted. For SQLite, the [official documentation](https://www.sqlite.org/lang_altertable.html) is a good place to start.
Best migrations on your evolving database journey! 👋
---
Article written by [Matthew Larkin](https://github.com/matthewlarkin) for [SQLPage](https://sql-page.com/).
@@ -0,0 +1,14 @@
select 'http_header' as component,
'public, max-age=300, stale-while-revalidate=3600, stale-if-error=86400' as "Cache-Control",
'<https://sql-page.com/your-first-sql-website/migrations>; rel="canonical"' as "Link";
select 'dynamic' as component, json_patch(json_extract(properties, '$[0]'), json_object(
'title', 'SQLPage migrations',
'description', 'Manage your database schema with SQLPage using migrations.'
)) as properties
FROM example WHERE component = 'shell' LIMIT 1;
-- Article by Matthew Larkin
select 'text' as component,
sqlpage.read_file_as_text('your-first-sql-website/migrations.md') as contents_md,
true as article;
@@ -0,0 +1,353 @@
# **Hosting SQLPage Behind a Reverse Proxy**
Hosting SQLPage behind a reverse proxy can help with security, scalability, and flexibility.
In this guide, we will guide you step-by-step on how to host SQLPage behind a reverse proxy using
[NGINX](https://www.nginx.com/).
## Why host SQLPage behind a Reverse Proxy ?
Here are some reasons why you might want to host SQLPage behind a reverse proxy:
- **customize your application's URLs**, removing `.sql` extensions and changing URL parameters
- **protect against attacks** such as denial-of-service (DoS) by rate limiting incoming requests
- **improve performance** by caching responses and serving static files without involving SQLPage
- **enable HTTPS** on the front-end, even when SQLPage is running on HTTP
- **host multiple applications** or multiple instances of SQLPage on the same server
## Prerequisites
Before you begin, you will need the following:
- A server running SQLPage. In this guide, we will assume SQLPage is running on `localhost:8080`
- Nginx installed on your server. On Ubuntu, you can install NGINX using `sudo apt install nginx`
- A domain name pointing to your server (optional)
- An SSL certificate obtained from Certbot (optional)
## Configuring the Reverse Proxy
NGINX uses a hierarchical configuration structure. The global configuration file (`/etc/nginx/nginx.conf`) contains settings that apply to the entire server, such as logging, caching, and rate limiting.Site-specific configuration files, stored in `/etc/nginx/sites-available/`, contain directives for individual websites or applications. These site-specific configurations are activated by creating symbolic links in the `/etc/nginx/sites-enabled/` directory. This setup allows for clean and organized management of multiple sites on a single server.
To host SQLPage behind a reverse proxy, you will need to create a new configuration file in the `/etc/nginx/sites-available/` directory, and then create a symbolic link to it in the `/etc/nginx/sites-enabled/` directory.
Create a file named `sqlpage` in the `/etc/nginx/sites-available/` directory:
```bash
sudo nano /etc/nginx/sites-available/sqlpage
```
Add the following configuration to the file:
```nginx
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://localhost:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}
```
Save the file and create a symbolic link to it in the `/etc/nginx/sites-enabled/` directory:
```bash
sudo ln -s /etc/nginx/sites-available/sqlpage /etc/nginx/sites-enabled/sqlpage
```
Test the configuration and reload NGINX:
```bash
sudo nginx -t
sudo systemctl reload nginx
```
Your SQLPage instance is now hosted behind a reverse proxy using NGINX. You can access it by visiting `http://example.com`.
### Streaming-friendly proxy settings
SQLPage streams HTML by default so the browser can render results while the database is still sending rows.
If you have slow SQL queries (you shouldn't), you can add the following directive to your location block:
```nginx
proxy_buffering off;
```
That will allow users to start seeing the top of your pages faster,
but will increase the load on your SQLPage server, and reduce the amount of users you can serve concurrently.
Refer to the official documentation for [proxy buffering](https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_buffering), [gzip](https://nginx.org/en/docs/http/ngx_http_gzip_module.html), and [chunked transfer](https://nginx.org/en/docs/http/ngx_http_core_module.html#chunked_transfer_encoding) when tuning these values.
When SQLPage sits behind a reverse proxy, set `compress_responses` to `false` [in `sqlpage.json`](https://github.com/sqlpage/SQLPage/blob/main/configuration.md) so that NGINX compresses once at the edge.
### URL Rewriting
URL rewriting is a powerful feature that allows you to manipulate URLs to make them more readable, search-engine-friendly, and easy to maintain.
In this section, we will cover how to use URL rewriting with SQLPage.
Note that for basic URL rewriting, you can use a simple [`404.sql`](/your-first-sql-website/custom_urls.sql) file to handle custom URLs.
However, for more complex rewriting rules, you can use NGINX's `rewrite` directive.
#### Example: Rewriting `/products/$id` to `/products.sql?id=$id`
Let's say you want your users to access product details using URLs like `/products/123` instead of `/products.sql?id=123`. This can be achieved using the `rewrite` directive in NGINX.
Here is an example configuration:
```nginx
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://localhost:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
location /products/ {
rewrite ^/products/([^/]+)$ /products.sql?id=$1 last;
}
}
```
This configuration uses the `rewrite` directive to rewrite URLs of the form `/products/$id` to `/products.sql?id=$id`. The `^/products/([^/]+)$` pattern matches URLs that start with `/products/` and captures the dynamic parameter `$id` using parentheses. The `last` flag indicates that this rewrite rule should be the last one to be applied; if the pattern matches, the rewritten URL is passed to the next location block,
in this case, the proxy_pass directive.
**How it Works**
When a request is made to `/products/123`, the rewrite rule is triggered, and the URL is rewritten to `/products.sql?id=123`. The `proxy_pass` directive then forwards the rewritten URL to the SQLPage instance, which processes the request and returns the response.
#### Example: Removing `.sql` Extension from URLs
Let's say you want to remove the `.sql` extension from all URLs to make them cleaner and more user-friendly. This can be achieved using the `rewrite` directive in NGINX.
```nginx
location / {
# When a request doesn't end with a '/' and doesn't have an extension, add '.sql' at the end
rewrite ^/((.*/)?[^/.]+)$ /$1.sql last;
proxy_pass http://localhost:8080;
}
```
### Hosting Multiple Applications
You may want to use the same web server to host SQLPage together with
another application such as a blog, a different website, or another instance of SQLPage.
In this section, we will cover how to host multiple applications behind a reverse proxy using NGINX.
#### Example: Hosting Two Applications with Different domain names
Let's say you want to host two separate instances of SQLPage on the same server, each accessible via a different domain name: `app1.example.com` and `app2.example.com`. This can be achieved by creating two separate configuration files in the `/etc/nginx/sites-available/` directory and then creating symbolic links to them in the `/etc/nginx/sites-enabled/` directory.
Create `/etc/nginx/sites-available/app1`, and `/etc/nginx/sites-available/app2` configuration files,
and add the following configuration to each file, replacing `localhost:8080` and `app1.example.com` with the appropriate values:
```nginx
server {
listen 80;
server_name app1.example.com;
location / {
proxy_pass http://localhost:8080;
}
}
```
Then create symbolic links to the configuration files in the `/etc/nginx/sites-enabled/` directory.
#### Hosting on a Subpath
You may have multiple applications to host, but a single domain name to use. In this case, you can host each application on a different subpath of the domain name, for example, `example.com/app1` and `example.com/app2`.
To host SQLPage on a subpath, you can use a single NGINX configuration file with a `location` block that specifies the subpath:
```nginx
server {
listen 80;
server_name example.com;
location /sqlpage {
proxy_pass http://localhost:8080;
}
}
```
This configuration sets up a reverse proxy that forwards incoming requests from `example.com/sqlpage` to `localhost:8080`, where SQLPage is running.
And in the SQLPage configuration file, at `./sqlpage/sqlpage.json`,
you can specify the base URL as `/sqlpage`:
```json
{
"site_prefix": "/sqlpage"
}
```
### IP Rate Limiting
To enable IP rate limiting for your SQLPage instance, you can use the
[`limit_req` module in NGINX](http://nginx.org/en/docs/http/ngx_http_limit_req_module.html).
Define a global rate limiting zone in `/etc/nginx/nginx.conf`:
```nginx
http {
...
limit_req_zone $binary_remote_addr zone=myzone:10m rate=10r/m;
}
```
Then use it in your site's configuration in `/etc/nginx/sites-available/sqlpage`:
```nginx
server {
...
location / {
limit_req zone=myzone;
proxy_pass http://localhost:8080;
...
}
}
```
This configuration sets up a reverse proxy that forwards incoming requests from `example.com` to `localhost:8080`, where SQLPage is running, and enables IP rate limiting to prevent abuse.
### Static File Serving
The [`try_files`](https://nginx.org/en/docs/http/ngx_http_core_module.html#try_files) directive in Nginx specifies the files to attempt to serve before falling back to a specified URI or passing the request to a proxy server. It's typically used within a location block to define the behavior when a request matches that location.
```nginx
server {
listen 80;
server_name example.com;
location ~ \.sql$ {
include sqlpage_proxy.conf;
}
location / {
try_files $uri @reverse_proxy;
}
location @reverse_proxy {
include sqlpage_proxy.conf;
}
}
```
And in `/etc/nginx/sqlpage_proxy.conf`:
```nginx
proxy_pass http://localhost:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
proxy_buffering on;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host;
```
### Caching and Buffering
To enable caching and buffering for your SQLPage instance, you can use the
[`proxy_cache`](https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_cache)
and [`proxy_buffering`](https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_buffering)
directives in NGINX.
Declare the cache in `/etc/nginx/nginx.conf` :
```nginx
http {
...
proxy_cache mycache;
# Cache settings: 1 hour for 200 and 302 responses, 1 minute for 404 responses
proxy_cache_valid 200 302 1h;
proxy_cache_valid 404 1m;
}
```
and then in your sqlpage nginx configuration file `/etc/nginx/sites-available/sqlpage` :
```nginx
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://sqlpage;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
proxy_cache mycache;
# Buffering: when a client is slow to read the response, quickly read the response from SQLPage and store it in a buffer, then send it to the slow client, while SQLPage can continue processing other requests
proxy_buffering on;
proxy_buffer_size 128k;
proxy_buffers 4 256k;
}
}
```
### **HTTPS and Certbot**
To let nginx handle HTTPS instead of SQLPage, you can obtain an SSL certificate from Certbot and configure nginx to use it.
Install certbot using the following command:
```bash
sudo snap install --classic certbot
```
Obtain an SSL certificate using the following command:
```bash
sudo certbot --nginx -d example.com
```
### Binding to a UNIX socket
Binding SQLPage to a Unix socket can reduce latency and enhance security by bypassing the network stack and restricting access to the socket file. Unix sockets are suitable for communication within the same host, offering lower overhead compared to TCP/IP.
#### SQLPage Configuration
Edit `./sqlpage/sqlpage.json`. Remove the `listen_on` and `port` configuration entries if they are present.
```json
{
"unix_socket": "/var/run/sqlpage.sock"
}
```
#### NGINX Configuration
In `/etc/nginx/sites-available/sqlpage`:
```nginx
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://unix:/var/run/sqlpage.sock;
proxy_set_header Host $host;
}
}
```
# Example NGINX configuration for SQLPage
You can find a fully working example of an NGINX configuration for SQLPage
illustrating all the features described in this guide
in the [examples/nginx](https://github.com/sqlpage/SQLPage/tree/main/examples/nginx)
directory of the SQLPage Github repository.
The example uses Docker and docker-compose to run NGINX and SQLPage.
@@ -0,0 +1,5 @@
select 'http_header' as component,
'public, max-age=300, stale-while-revalidate=3600, stale-if-error=86400' as "Cache-Control";
select 'dynamic' as component, properties FROM example WHERE component = 'shell' LIMIT 1;
select 'text' as component, sqlpage.read_file_as_text('your-first-sql-website/nginx.md') as contents_md;
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 398 KiB

@@ -0,0 +1,25 @@
# Download SQLPage
SQLPage is a small single-file program that will
execute the SQL files you write,
and render the database responses as nice web pages.
If you have already downloaded SQLPage,
you can skip this step and [start writing your website](#tutorial).
[Download the latest SQLPage](https://github.com/sqlpage/SQLPage/releases) for your operating system.
In the _release assets_ section, you will find files named `sqlpage-windows.zip`, `sqlpage-linux.tgz`, and `sqlpage-macos.tgz`.
Download the one that corresponds to your operating system, and extract the executable file from the archive.
> **Note**: On Mac OS, Apple blocks the execution of downloaded files by default. The easiest way to run SQLPage is to use [Homebrew](https://brew.sh).
> **Note**: Advanced users can alternatively install SQLPage using:
> - [docker](https://hub.docker.com/repository/docker/lovasoa/SQLPage/general) (docker images are also available for ARM, making it easy to run SQLPage on a Raspberry Pi, for example),
> - [brew](https://formulae.brew.sh/formula/sqlpage) (the easiest way to install SQLPage on Mac OS),
> - [nix](https://search.nixos.org/packages?channel=unstable&show=sqlpage) (declarative package management for reproducible deployments),
> - [scoop](https://scoop.sh/#/apps?q=sqlpage&id=305b3437817cd197058954a2f76ac1cf0e444116) (a command-line installer for Windows),
> - or [cargo](https://crates.io/crates/sqlpage) (the Rust package manager).
You can also find the source code of SQLPage on [GitHub](https://github.com/sqlpage/SQLPage), [install rust](https://www.rust-lang.org/tools/install) on your computer, and compile it yourself with `cargo install sqlpage`.
See the instructions for [MacOS](?os=macos#download), or for [Windows](?os=windows#download).
@@ -0,0 +1,18 @@
# Download SQLPage for Mac OS
On Mac OS, Apple blocks the execution of downloaded files by default. The easiest way to run SQLPage is to use [Homebrew](https://brew.sh).
Open a terminal and run the following commands:
```sh
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
brew install sqlpage
sqlpage
```
> **Note**: Advanced users can alternatively install SQLPage using
> [the precompiled binaries](https://github.com/sqlpage/SQLPage/releases/latest),
> [docker](https://hub.docker.com/repository/docker/lovasoa/SQLPage/general),
> [nix](https://search.nixos.org/packages?channel=unstable&show=sqlpage),
> or [cargo](https://crates.io/crates/sqlpage).
> **Not on Mac OS?** See the instructions for [Windows](?os=windows#download), or for [Other Systems](?os=any#download).
@@ -0,0 +1,14 @@
# Download the SQLPage executable for Windows
SQLPage offers a small executable file (`sqlpage.exe`) that will take requests to your website,
execute the SQL files you write, and render the database responses as nice web pages.
[Download the latest SQLPage for Windows](https://github.com/sqlpage/SQLPage/releases/latest/download/sqlpage-windows.zip).
Download the file, and extract the executable file from the zip archive.
> **Note**: Advanced users can alternatively install SQLPage using
> [docker](https://hub.docker.com/repository/docker/lovasoa/SQLPage/general),
> [scoop](https://scoop.sh/#/apps?q=sqlpage&id=305b3437817cd197058954a2f76ac1cf0e444116),
> or [cargo](https://crates.io/crates/sqlpage).
> **Not on Windows?** See the instructions for [Mac OS](?os=macos#download), or for [Other Systems](?os=any#download).
@@ -0,0 +1,199 @@
# Building your website locally
Create a folder on your computer where you will store all contents related to your sql website.
In the rest of this tutorial, we will call this folder the **root folder** of your website.
- On **Windows**, place the `sqlpage.exe` you downloaded above at the root of the folder. Then double-click the `sqlpage.exe` file to start the server.
- On **Linux**, place `sqlpage.bin` at the root of the folder. Then open a terminal, cd to the root folder of your website, and run `./sqlpage.bin` to start the server.
- On **Mac OS**, if you installed SQLPage using Homebrew, then you do not need to place anything at the root of the folder. Open Terminal, cd to the root folder of your website, and type `sqlpage` to start the server.
![screenshot for the sql website setup on linux](first-sql-website-launch.png)
You should see a message in your terminal telling you that SQLPage is ready, and giving you the address of your website.
You can open your website locally by visiting [`http://localhost:8080`](http://localhost:8080)
SQLPage should have automatically created a folder called `sqlpage` with a SQLite database file named `sqlpage.db`. This is your website's default database - don't worry, we'll learn how to connect to other databases like PostgreSQL, MySQL, or SQL Server later!
# Your website's first SQL file
In the root folder of your SQLPage website, create a new SQL file called `index.sql`.
Open it in a text editor that supports SQL syntax highlighting (I recommend [VSCode](https://code.visualstudio.com/)).
The `index.sql` file will be executed every time a visitor opens your website's home page, and the results will be displayed to the visitor
using the components you specify in the file.
Let's start with a simple `index.sql` that displays a list of popular websites:
```sql
SELECT 'list' AS component,
'Popular websites' AS title;
SELECT 'Hello' AS title,
'world' AS description,
'https://wikipedia.org' AS link;
```
![screenshot of the first sql website](hello-world.png)
The first line of the file defines the component that will be used to display the data, and properties of that component.
In this case, we use the [`list` component](/component.sql?component=list) to display a list of items.
The second line defines the data that will populate the component.
All the components you can use and their properties are documented in [SQLPage's online documentation](https://sql-page.com/documentation.sql).
# Your database schema
> If you already have a database populated with data,
> or if you intend to use other tools to manage your database structure,
> you can skip this section.
The [database schema](https://en.wikipedia.org/wiki/Database_schema) for your SQLPage website
can be defined using SQL scripts located in the
**`sqlpage/migrations`** subdirectory of your website's root folder.
For our first website, let's create a file located in `sqlpage/migrations/0001_create_users_table.sql` with the following contents:
```sql
CREATE TABLE users (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL
);
```
If you need to quickly test a database schema and associated queries online,
before making any change to your database, I can recommend [sqliteonline.com](https://sqliteonline.com/) (which actually also works with Postgres, MySQL, and SQL Server).
Please read our [**introduction to database migrations**](./migrations.sql) to
learn how to maintain your database schema in the long term.
> **Note**: The migration system is not supported on Microsoft SQL Server databases.
> If you are using a SQL Server database, you should create your tables using a different tool, such as _SQL Server Management Studio_.
# Connect to a custom database
By default, SQLPage uses a [SQLite](https://www.sqlite.org/about.html) database stored in a file named `sqlpage.db`
in the `sqlpage` configuration folder.
You can change this by creating a file named `sqlpage.json` in a folder called `sqlpage`.
So, if your website's root folder is `/my_website`, you should create a file at `/my_website/sqlpage/sqlpage.json`.
Here is an example `sqlpage.json` file:
```sql
{ "database_url": "sqlite://:memory:" }
```
This will tell SQLPage to use an in-memory SQLite database instead of the default file-based database. While this means all changes to the database will be lost when you stop the SQLPage server, it's useful for quickly testing and iterating on your database schema.
If you then deploy your website online using a service like [DataPage.app](https://datapage.app), it will automatically use a persisted database instead.
Later, when you want to deploy your website online, you can switch back to a persistent database like
- a SQLite file with `sqlite://your-database-file.db` ([see options](https://docs.rs/sqlx-oldapi/latest/sqlx_oldapi/sqlite/struct.SqliteConnectOptions.html)),
- a PostgreSQL-compatible server with `postgres://user:password@host/database` ([see options](https://docs.rs/sqlx-oldapi/latest/sqlx_oldapi/postgres/struct.PgConnectOptions.html)),
- a MySQL-compatible server with `mysql://user:password@host/database` ([see options](https://docs.rs/sqlx-oldapi/latest/sqlx_oldapi/mysql/struct.MySqlConnectOptions.html)),
- a Microsoft SQL Server with `mssql://user:password@host/database` ([see options](https://docs.rs/sqlx-oldapi/latest/sqlx_oldapi/mssql/struct.MssqlConnectOptions.html#method.from_str), [note about named instances](https://github.com/sqlpage/SQLPage/issues/92)),
- any ODBC-compatible database like DuckDB, ClickHouse, Databricks, Snowflake, BigQuery, Oracle, Db2, and many more. See [ODBC database connection instructions](https://github.com/sqlpage/SQLPage#odbc-setup).
> If `user` or `password` **contains special characters**, you should [**percent-encode**](https://en.wikipedia.org/wiki/Percent-encoding) them.
>
> For instance, a SQL Server database named `db` running on `localhost` port `1433` with the username `funny:user` and the password `p@ssw0rd` would be represented as
> `mssql://funny%3Auser:p%40ssw0rd@localhost:1433/db`.
For more information about the properties that can be set in sqlpage.json, see [SQLPage's configuration documentation](https://github.com/sqlpage/SQLPage/blob/main/configuration.md#configuring-sqlpage)
![screenshot for the full sql website folder organisation](full-website.png)
# Use parameterized SQL queries to let users interact with your database
### Displaying a form
Let's create a form to let our users insert data into our database. Add the following code to your `index.sql` file:
```sql
SELECT 'form' AS component, 'Add a user' AS title;
SELECT 'Username' as name, TRUE as required;
```
The first SELECT statement opens the [`form` component](https://sql-page.com/component.sql?component=form).
The second SELECT statement adds a field to the form. Since we do not specify a `type`, it will be a text field. The label displayed above the field will be the same as its name by default.
### Handling form submission
Nothing happens when you submit the form at the moment. Let's fix that.
Add the following below the previous code:
```sql
INSERT INTO users (name)
SELECT :Username
WHERE :Username IS NOT NULL;
```
The snippet above uses an [`INSERT INTO SELECT` SQL statement](https://www.sqlite.org/lang_insert.html) to
[safely](../safety.sql) insert a new row into the `users` table when the form is submitted.
It uses a `WHERE` clause to make sure that the `INSERT` statement is only executed when the `:Username` parameter is present.
The `:Username` parameter is set to `NULL` when you initially load the page, and then SQLPage automatically sets it to the value
from the text field when the user submits the form.
#### Parameters
There are two types of parameters you can use in your SQL queries:
- **URL parameters** like **`$ParameterName`**. If you add `?x=1&y=2` to the end of the URL of your page, `$x` will be set to the string `'1'` and `$y` will be set to the string `'2'`. This is useful to create links with parameters. For instance, if you have a database of products, you can create a link to a product page with the URL `product?product_id=12` (or `product.sql?product_id=12` - both work). Then, in the `product.sql` file, you can use the `$product_id` variable to get the product with the corresponding ID from your database. URL parameters are also sometimes called *query parameters*, or *GET parameters*.
- **Form parameters** like **`:ParameterName`**. They refer to the value of the field with the corresponding `name` entered by the user in a [form](/component.sql?component=form). If no form was submitted, it is set to `NULL`. Form parameters are also sometimes called *POST parameters*.
> Note: Currently, if a `$parameter` is not present in the URL, it is first looked for in the form parameters. If it is not found there either, it is set to `NULL`. Please do not rely on this behavior, as it may change in the future.
You can also set parameters yourself at any point in your SQL files in order to reuse
their value in several places, using the `SET ParameterName = value` syntax.
For instance, we could use the following code to save the username in uppercase:
```sql
SET Username = UPPER(:Username);
INSERT INTO users (name) VALUES ($Username);
```
### Displaying data from our database
Now, users are present in our database, but we can't see them.
Let's see how to use data from our database to populate a [list](/component.sql?component=list) component, in order to display the list of users.
Add the following code to your `index.sql` file:
```sql
SELECT 'list' AS component, 'Users' AS title;
SELECT name AS title, CONCAT(name, ' is a user on this website.') as description FROM users;
```
### Your first SQLPage website is ready!
You can view [the full source code for this example on Github](https://github.com/sqlpage/SQLPage/tree/main/examples/simple-website-example)
Here is a screenshot of the final result:
![final result](final-result.png)
To go further, have a look at [the examples](../examples/).
# Deploy your SQLPage website online
### Using DataPage.app
To deploy your SQLPage website online, the easiest way is to use [DataPage.app](https://datapage.app),
a managed hosting service for SQLPage websites maintained by the same people who develop SQLPage.
Just create an account, and follow the instructions to upload your website to our servers. It will be live in seconds!
### Manually
If you prefer to host your website yourself, you can use a cloud provider or a VPS provider. You will need to:
- Configure domain name resolution to point to your server
- Open the port you are using (8080 by default) in your server's firewall
- [Setup docker](https://github.com/sqlpage/SQLPage?tab=readme-ov-file#with-docker) or another process manager such as [systemd](https://github.com/sqlpage/SQLPage/blob/main/sqlpage.service) to start SQLPage automatically when your server boots and to keep it running
- Optionally, [setup a reverse proxy](nginx.sql) to avoid exposing SQLPage directly to the internet
- Optionally, setup a TLS certificate to enable HTTPS
- Configure connection to a cloud database or a database running on your server in [`sqlpage.json`](https://github.com/sqlpage/SQLPage/blob/main/configuration.md#configuring-sqlpage)
# Go further
- Check out [learnsqlpage.com](https://learnsqlpage.com) by Nick Antonaccio for an in-depth tutorial with many examples
- Read the [SQLPage documentation](/documentation.sql) to learn about all the components available in SQLPage
- Read about [SQLPage's extensions to SQL](/extensions-to-sql) for a specification of the SQL syntax you can use in SQLPage, the data types used when exchanging data with the browser and with the database, a clear explanation of how *SQLPage variables* and *SQLPage functions* work.
- Join the [SQLPage community](https://github.com/sqlpage/SQLPage/discussions) to ask questions and share your projects
- If you like videos better, check this series that shows how to build and deploy your app from scratch [SQLPage on Youtube](https://www.youtube.com/playlist?list=PLTue_qIAHxAf9fEjBY2CN0N_5XOiffOk_)