Gleam Lustre Fullstack App#
A fullstack demo application built with Gleam and Lustre. Users can authenticate via OAuth, view and edit their profiles.
Features#
- OAuth 2.0 Authentication - Secure authentication with PKCE flow for Bluesky/ATProto
- Profile Management - View and edit user profiles with display name, description, avatar, location, and interests
- Avatar Upload - Upload and preview profile images with decentralized blob storage
- Location Search - Autocomplete location selection with H3 geohashing for geographic indexing
- Server-Side Rendering - Fast initial page loads with prerendered profile data
- Client-Side Routing - Smooth navigation with Modem routing library
- Session Management - Secure cookie-based sessions with SQLite storage
- GraphQL Integration - Direct integration with Slices network API for ATProto data
Tech Stack#
Frontend (Client)#
- Gleam - Type-safe functional language compiled to JavaScript
- Lustre - Elm-inspired web framework for reactive UIs
- Modem - Client-side routing
- h3-js - H3 geohashing for location indexing
- Tailwind CSS - Utility-first CSS framework
Backend (Server)#
- Gleam - Compiled to Erlang/BEAM
- Wisp - Web framework for request handling
- Mist - HTTP server runtime
- SQLight - SQLite database driver
- Storail - Session management
- Glow Auth - OAuth utilities
- Squall - Type-safe GraphQL code generator
Shared#
- Monorepo structure with shared types and utilities between client and server
Prerequisites#
- Gleam (latest version)
- Erlang/OTP (for server)
- Node.js and npm (for client dependencies)
Project Structure#
lustre-fullstack/
├── client/ # Lustre frontend (SPA)
│ ├── src/
│ │ ├── client.gleam # Main entry point
│ │ ├── pages/ # Page components
│ │ └── ui/ # Reusable UI components
│ ├── gleam.toml
│ └── package.json
├── server/ # Wisp backend
│ ├── src/
│ │ ├── server.gleam # Main server & routing
│ │ ├── api/ # API handlers
│ │ └── oauth/ # OAuth & session logic
│ ├── gleam.toml
│ ├── .env.example
│ └── priv/static/ # Built client output
└── shared/ # Shared code between client & server
└── src/shared/
└── profile.gleam # Profile types & codecs
Setup#
1. Clone the Repository#
git clone <repository-url>
cd conf-demo
2. Set Up Server#
cd server
# Install Gleam dependencies
gleam deps download
# Copy environment template
cp .env.example .env
# Edit .env with your configuration
# Generate SECRET_KEY_BASE with: openssl rand -base64 48
3. Set Up Client#
cd client
# Install Gleam dependencies
gleam deps download
# Install npm dependencies (h3-js)
npm install
4. Set Up Shared Package#
cd shared
gleam deps download
Configuration#
Edit server/.env with your OAuth and application settings:
# Secret Key Base (generate with: openssl rand -base64 48)
SECRET_KEY_BASE=your_random_64_character_string
# OAuth Configuration
OAUTH_CLIENT_ID=your_oauth_client_id
OAUTH_CLIENT_SECRET=your_oauth_client_secret
OAUTH_REDIRECT_URI=http://localhost:3000/oauth/callback
OAUTH_AUTH_URL=https://auth.slices.network
Development#
Build Client#
The client builds directly into server/priv/static for serving:
cd client
gleam run
This compiles the Lustre app with minification and outputs to ../server/priv/static.
Run Server#
cd server
gleam run
The server starts on http://localhost:3000 and serves the built client from priv/static.
Development Workflow#
- Make changes to client code in
client/src/ - Rebuild client:
cd client && gleam run - Server automatically serves updated static files
- For server changes, restart the server
Alternatively, run both in separate terminals with auto-rebuild on file changes using your preferred file watcher.
API Endpoints#
Public Routes#
GET /- Home pageGET /login- Login pageGET /profile/:handle- View profile (with SSR)POST /oauth/authorize- Initiate OAuth flowGET /oauth/callback- OAuth callback
Protected Routes (Require Authentication)#
GET /profile/:handle/edit- Edit profile pagePOST /api/profile/:handle/update- Update profile dataPOST /logout- Logout
API Routes#
GET /api/user/current- Get current authenticated userGET /api/profile/:handle- Fetch profile data (JSON)
Database#
The application uses SQLite for session storage:
- Database file:
server/sessions.db(auto-created on first run) - Schema managed by
server/src/oauth/session.gleam
Features in Detail#
OAuth with PKCE#
Implements OAuth 2.0 with Proof Key for Code Exchange (PKCE) for secure authentication without storing client secrets in the browser.
H3 Geohashing#
Location data is indexed using Uber's H3 geospatial indexing system for efficient location queries and autocomplete.
Server-Side Rendering#
Profile pages include prerendered data in the initial HTML response, embedded as JSON in a script tag for instant hydration.
GraphQL Integration#
Type-safe GraphQL queries and mutations generated from .gql files using Squall. All GraphQL operations are defined in server/src/api/graphql/ and automatically generate type-safe Gleam code with decoders and input types.
To regenerate GraphQL code after modifying .gql files:
cd server
make generate-graphql
Building for Production#
Client#
cd client
gleam run # Outputs minified bundle to ../server/priv/static
Server#
cd server
gleam build
gleam run
The server serves the built client from priv/static at runtime.
Testing#
# Test client
cd client
gleam test
# Test server
cd server
gleam test
# Test shared
cd shared
gleam test
Contributing#
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
License#
Apache License, Version 2.0