Skip to content

Commit 9861622

Browse files
author
Daniele Briggi
committed
feat(example): sport tracker app initial commit
1 parent 7fe2c47 commit 9861622

27 files changed

+4745
-2
lines changed

.gitignore

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@
44
*.xcbkptlist
55
*.plist
66
/build
7-
/dist
7+
**/dist/**
88
/coverage
99
*.sqlite
1010
*.a
1111
unittest
1212
/curl/src
13-
.vscode
13+
.vscode
14+
**/node_modules/**
15+
.env
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Copy from from the SQLite Cloud Dashboard
2+
# eg: sqlitecloud://myhost.cloud:8860/my-remote-database.sqlite
3+
VITE_SQLITECLOUD_CONNECTION_STRING=
4+
# The database name
5+
# eg: my-remote-database.sqlite
6+
VITE_SQLITECLOUD_DATABASE=
7+
# Your SQLite Cloud API key
8+
# Copy it from the SQLite Cloud Dashboard -> Settings -> API Keys
9+
VITE_SQLITECLOUD_API_KEY=
10+
# Your SQLite Cloud url for APIs
11+
# Get it from the SQLite Cloud Dashboard in the Weblite section
12+
# eg: https://myhost.cloud
13+
VITE_SQLITECLOUD_API_URL=
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
# Sport Tracker app with SQLite Sync 🚵
2+
3+
A Vite/React demonstration app showcasing [**SQLite Sync**](https://github.com/sqliteai/sqlite-sync) implementation for **offline-first** data synchronization across multiple devices. This example illustrates how to integrate SQLite AI's sync capabilities into modern web applications with proper authentication via [Access Token](https://docs.sqlitecloud.io/docs/access-tokens) and [Row-Level Security (RLS)](https://docs.sqlitecloud.io).
4+
5+
6+
## Features
7+
8+
From a **user experience** perspective, this is a simple sport tracking application where users can:
9+
- Create accounts and log activities (running, cycling, swimming, etc.)
10+
- View personal statistics and workout history
11+
- Access "Coach Mode" for managing multiple users' workouts
12+
13+
From a **developer perspective**, this app showcases:
14+
- **Offline-first** architecture with sync to the remote database using **SQLite Sync** extension for SQLite
15+
- **Row-Level Security (RLS)** implementation for data isolation and access control on the SQLite Cloud database
16+
- **Access Tokens** for secure user authentication with SQLite Sync and RLS policy enforcement
17+
- **Multi-user** data isolation and sharing patterns across different user sessions
18+
19+
## Setup Instructions
20+
21+
### 1. Prerequisites
22+
- Node.js 18+
23+
- [SQLite Cloud account](https://sqlitecloud.io)
24+
25+
### 2. Database Setup
26+
1. Create database in [SQLite Cloud Dashboard](https://dashboard.sqlitecloud.io/).
27+
2. Execute the exact schema from `sport-tracker-schema.sql`.
28+
3. Enable OffSync for all tables on the remote database from the **SQLite Cloud Dashboard -> Databases**.
29+
4. Enable and configure RLS policies on the **SQLite Cloud Dashboard -> Databases**. See the file `rls-policies.md`.
30+
31+
### 3. Environment Configuration
32+
33+
Rename the `.env.example` into `.env` and fill with your values.
34+
35+
### 4. Installation & Run
36+
37+
```bash
38+
npm install
39+
npm run dev
40+
```
41+
42+
> This app uses the packed WASM version of SQLite with the [SQLite Sync extension enabled](https://www.npmjs.com/package/@sqliteai/sqlite-sync-wasm).
43+
44+
## Demo Use Case: Multi-User Sync Scenario
45+
46+
This walkthrough demonstrates how SQLite Sync handles offline-first synchronization between multiple users:
47+
48+
### The Story: Bob the Runner & Coach Sarah
49+
50+
1. **Bob starts tracking offline** 📱
51+
- Open [localhost:5173](http://localhost:5173) in your browser
52+
- Create user `bob` and add some activities
53+
- Notice Bob's data is stored locally - no internet required!
54+
55+
2. **Bob goes online and syncs** 🌐
56+
- Click `SQLite Sync` to authenticate SQLite Sync
57+
- Click `Sync & Refresh` - this generates an Access Token and synchronizes Bob's local data to the cloud
58+
- Bob's activities are now replicated in the cloud
59+
60+
3. **Coach Sarah joins from another device** 👩‍💼
61+
- Open a new private/incognito browser window at [localhost:5173](http://localhost:5173)
62+
- Create user `coach` (this triggers special coach privileges via RLS)
63+
- Enable `SQLite Sync` - Coach can now see Bob's synced activities thanks to RLS policies
64+
65+
4. **Coach creates a workout for Bob** 💪
66+
- Coach creates a workout assigned to Bob
67+
- Click `Sync & Refresh` to upload the workout to the cloud
68+
69+
5. **Bob receives his workout** 📲
70+
- Go back to Bob's browser window
71+
- Click `Sync & Refresh` - Bob's local database downloads the new workout from Coach
72+
- Bob can now see his personalized workout
73+
74+
6. **Bob gets a new device** 📱➡️💻
75+
- Log out Bob, then select it and click `Restore from cloud`
76+
- This simulates Bob logging in from a completely new device with no local data
77+
- Enable `SQLite Sync` and sync - all of Bob's activities and workouts are restored from the cloud
78+
79+
**Key takeaway**: Users can work offline, sync when convenient, and seamlessly restore data on new devices!
80+
81+
82+
## SQLite Sync Implementation
83+
84+
### 1. Database Initialization
85+
86+
```typescript
87+
// database.ts - Initialize sync for each table
88+
export class Database {
89+
async initSync() {
90+
await this.exec('SELECT cloudsync_init("users")');
91+
await this.exec('SELECT cloudsync_init("activities")');
92+
await this.exec('SELECT cloudsync_init("workouts")');
93+
}
94+
}
95+
```
96+
97+
### 2. Token Management
98+
99+
```typescript
100+
// SQLiteSync.ts - Access token handling
101+
private async getValidToken(userId: string, name: string): Promise<string> {
102+
const storedTokenData = localStorage.getItem('token');
103+
104+
if (storedTokenData) {
105+
const parsed: TokenData = JSON.parse(storedTokenData);
106+
const tokenExpiry = new Date(parsed.expiresAt);
107+
108+
if (tokenExpiry > new Date()) {
109+
return parsed.token; // Use cached token
110+
}
111+
}
112+
113+
// Fetch new token from API
114+
const tokenData = await this.fetchNewToken(userId, name);
115+
localStorage.setItem('token', JSON.stringify(tokenData));
116+
return tokenData.token;
117+
}
118+
```
119+
120+
Then authorize SQLite Sync with the token. This operation is executed again when tokens expire and a new one is provided.
121+
122+
```typescript
123+
async sqliteSyncSetToken(token: string) {
124+
await this.exec(`SELECT cloudsync_network_set_token('${token}')`);
125+
}
126+
```
127+
128+
### 3. Synchronization
129+
130+
The sync operation sends local changes to the cloud and receives remote changes:
131+
132+
```typescript
133+
async sqliteSyncNetworkSync() {
134+
await this.exec('SELECT cloudsync_network_sync()');
135+
}
136+
```
137+
138+
## Row-Level Security (RLS)
139+
140+
This app demonstrates **Row-Level Security** configured in the SQLite Cloud Dashboard. RLS policies ensure:
141+
142+
- **Users** can only see their own activities and workouts
143+
- **Coaches** can access all users' data and create workouts for the users
144+
- **Data isolation** is enforced at the database level
145+
146+
### Example RLS Policies
147+
148+
```sql
149+
-- Policy for selecting activities
150+
auth_userid() = user_id OR json_extract(auth_json(), '$.name') = 'coach'
151+
152+
-- Policy for inserting into workouts table
153+
json_extract(auth_json(), '$.name') = 'coach'
154+
```
155+
156+
> **Note**: Configure RLS policies in your SQLite Cloud Dashboard under Databases → RLS
157+
158+
## Security Considerations
159+
160+
⚠️ **Important**: This demo includes client-side API key usage for simplicity. In production:
161+
162+
- Never expose API keys in client code
163+
- Use **server-side generation** for Access Tokens
164+
- Implement a proper authentication flow
165+
166+
## Documentation Links
167+
168+
Explore the code and learn more:
169+
170+
- **SQLite Sync Documentation**: [sqlite-sync](https://github.com/sqliteai/sqlite-sync)
171+
- **Access Tokens Guide**: [SQLite Cloud Access Tokens](https://docs.sqlitecloud.com/authentication/access-tokens)
172+
- **Row-Level Security**: [SQLite Cloud RLS](https://docs.sqlitecloud.com)
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
6+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
7+
<title>Sport Tracker</title>
8+
</head>
9+
<body>
10+
<div id="root"></div>
11+
<script type="module" src="/src/main.tsx"></script>
12+
</body>
13+
</html>

0 commit comments

Comments
 (0)