Production-ready Elixir/Phoenix backend template with Flowless integration, featuring WebSocket support and modern async patterns.
- Bridge Validation - Distributed session validation with Flowless
- Auth Middleware - Pipeline-based route protection (required/optional)
- WebSocket Channels - Real-time communication with auth integration
- Multi-Database - Support for PostgreSQL (including CockroachDB), MySQL, and SQLite
- Environment Config - .env file loading with Dotenvy
- Ecto ORM - Database operations with optional Repo (starts only if DATABASE_URL set)
- Interactive Testing - Built-in WebSocket test page at
/ws
- Elixir 1.14+ - Functional programming with OTP
- Phoenix 1.8+ - Modern web framework
- Bandit - High-performance HTTP/2 server
- Ecto 3.x - Database wrapper and query DSL
- Phoenix Channels - WebSocket communication
- Dotenvy - Environment configuration
- Req - HTTP client for bridge validation
# Clone the repository
git clone <repository-url>
cd flowfull_elixir_starter
# Install dependencies
mix deps.get
# Compile assets (first time only)
mix assets.setup# Copy environment template
cp .env.example .env
# Edit .env with your configuration
nano .envMinimum required configuration:
FLOWLESS_API_URL=https://api.pubflow.com
BRIDGE_VALIDATION_SECRET=your-shared-secret-min-32-charsOptional configuration (for database):
DATABASE_URL=postgresql://user:pass@localhost:5432/flowfull_devIf using a database:
# Create database
mix ecto.create
# Run migrations
mix ecto.migrate# Start server with hot reload
mix phx.server
# Or start with IEx shell for debugging
iex -S mix phx.server
# Server will be available at:
# - Home: http://localhost:4000
# - Health: http://localhost:4000/api/health
# - WebSocket Test: http://localhost:4000/wsGET /- Home pageGET /api/health- Health checkGET /api/public- Public contentGET /api/content- Mixed content (optional auth)
GET /api/secure/me- Current user informationGET /api/secure/profile- User profileGET /api/secure/protected- Protected resource
GET /api/secure/admin/dashboard- Admin dashboardGET /api/secure/admin/users- List all users (DB query)GET /api/secure/admin/users/:id- Get user by ID (DB query)GET /api/secure/users- List all users (alias)GET /api/secure/users/:id- Get user by ID (alias)
Visit http://localhost:4000/ws for an interactive WebSocket test page.
Features:
- Connect with or without session authentication
- Join public channels (no auth required)
- Join secure channels (auth required)
- Send custom events
- View real-time messages
Public Channels (public:*)
- No authentication required
- Example:
public:lobby - Auto-joins on connection in test page
Secure Channels (secure:*)
- Requires valid session authentication
- Example:
secure:user_room - Only accessible with
X-Session-Idheader orsession_idquery param
Pass session ID via query parameter:
const socket = new Phoenix.Socket("/socket", {
params: {session_id: "your-session-id-here"}
});Or use the test page at /ws which handles authentication automatically.
Comprehensive documentation is available in the /docs directory:
- 00-ARCHITECTURE-OVERVIEW.md - System architecture and components
- 02-CORE-CONCEPTS.md - Core concepts with examples
- 03-ENVIRONMENT.md - Environment configuration guide
- 04-USAGE-GUIDE.md - Complete usage examples
- QUICK-REFERENCE.md - Quick reference guide
# In router.ex
scope "/api", MyAppWeb do
pipe_through :api
get "/hello", MyController, :hello
end
# In controller
def hello(conn, _params) do
json(conn, %{message: "Hello, world!"})
end# In router.ex
scope "/api/secure", MyAppWeb do
pipe_through :api_auth
get "/profile", MyController, :profile
end
# In controller
def profile(conn, _params) do
claims = conn.assigns[:auth_claims]
json(conn, %{
user_id: claims["user_id"],
email: claims["email"],
name: claims["name"]
})
end# Uses :api pipeline (OptionalMiddleware)
scope "/api", MyAppWeb do
pipe_through :api
get "/content", MyController, :content
end
# In controller
def content(conn, _params) do
claims = conn.assigns[:auth_claims]
if claims do
json(conn, %{message: "Hello, #{claims["name"]}"})
else
json(conn, %{message: "Hello, guest"})
end
endscope "/api/secure", MyAppWeb do
pipe_through :api_auth
get "/admin/reports", MyController, :admin_reports
end
# In controller
def admin_reports(conn, _params) do
claims = conn.assigns[:auth_claims] || %{}
if admin?(claims) do
json(conn, %{reports: []})
else
conn
|> put_status(403)
|> json(%{error: "forbidden"})
end
end
defp admin?(%{"user_type" => type}) when type in ["admin", "superadmin"], do: true
defp admin?(_), do: falsealias MyApp.Repo
alias MyApp.Models.User
# Query all users
users = Repo.all(User)
# Get by ID
user = Repo.get(User, "user-id")
# Query with filter
import Ecto.Query
active_users = from(u in User, where: u.active == true) |> Repo.all()
# Insert
%User{id: "new-id", email: "test@example.com"} |> Repo.insert()
# Handle optional DB
try do
users = Repo.all(User)
json(conn, users)
rescue
RuntimeError ->
conn
|> put_status(503)
|> json(%{error: "database not configured"})
enddefmodule MyAppWeb.ChatChannel do
use MyAppWeb, :channel
# Public channel
def join("chat:lobby", _payload, socket) do
{:ok, socket}
end
# Secure channel (auth required)
def join("chat:private", _payload, socket) do
if socket.assigns[:auth_claims] do
{:ok, socket}
else
{:error, %{reason: "unauthorized"}}
end
end
def handle_in("new_msg", %{"body" => body}, socket) do
broadcast!(socket, "new_msg", %{body: body})
{:noreply, socket}
end
end# Format code
mix format
# Check for compilation warnings
mix compile --warnings-as-errors
# Run precommit checks (format + tests)
mix precommit# Run all tests (no database required)
mix test
# Run specific test file
mix test test/flowfull_elixir_starter_web/controllers/page_controller_test.exs
# Run with verbose output
mix test --traceNote: Tests run without database by default. If you need to test DB-dependent features, ensure DATABASE_URL is not set in test environment or use a SQLite database for testing.
Coverage: This starter template includes basic tests. You can add more comprehensive tests for your application as needed.
flowfull_elixir_starter/
βββ lib/
β βββ flowfull_elixir_starter/
β β βββ auth/
β β β βββ bridge_validator.ex # Session validation
β β β βββ middleware.ex # Required auth
β β β βββ optional_middleware.ex # Optional auth
β β βββ models/
β β β βββ user.ex # User model
β β βββ repo.ex # Database repo
β βββ flowfull_elixir_starter_web/
β β βββ channels/
β β β βββ public_channel.ex # Public WS channel
β β β βββ secure_channel.ex # Secure WS channel
β β βββ controllers/
β β β βββ page_controller.ex # Pages (/ws test)
β β β βββ sample_controller.ex # API routes
β β βββ endpoint.ex # HTTP endpoint
β β βββ router.ex # Route definitions
β β βββ user_socket.ex # WebSocket handler
βββ config/
β βββ config.exs # Compile-time config
β βββ dev.exs # Dev config
β βββ test.exs # Test config
β βββ runtime.exs # Runtime config (Dotenvy)
βββ priv/
β βββ repo/
β βββ migrations/ # Database migrations
βββ test/ # Tests
βββ docs/ # Documentation
βββ .env.example # Environment template
βββ mix.exs # Project definition
# Send session ID in header
curl http://localhost:4000/api/secure/profile \
-H "X-Session-Id: your-session-id-here"
# Or use cookie (alternative)
curl http://localhost:4000/api/secure/profile \
-H "Cookie: session_id=your-session-id-here"Note: Header names are automatically normalized to lowercase by Plug (X-Session-Id β x-session-id).
For local development without Flowless:
DEV_ALLOW_BRIDGE_STUB=1This bypasses real bridge validation and returns mock user data.
Required for production:
FLOWLESS_API_URL=https://your-instance.pubflow.com
BRIDGE_VALIDATION_SECRET=your-production-secret-min-32-chars
DATABASE_URL=postgresql://user:pass@host:5432/db
SECRET_KEY_BASE=your-phoenix-secret-key-base
PORT=4000mix phx.gen.secret# Build release
MIX_ENV=prod mix release
# Run release
_build/prod/rel/flowfull_elixir_starter/bin/flowfull_elixir_starter startOr use Docker for deployment.
- Fork the repository
- Create a feature branch
- Make your changes
- Run tests and formatting (
mix test && mix format) - Submit a pull request
MIT License - see LICENSE file for details
- Flowless Documentation: Flowless Docs
- Phoenix Documentation: Phoenix Framework
- Elixir Documentation: Elixir Lang
- Ecto Documentation: Ecto
For issues and questions:
- Open an issue on GitHub
- Check the documentation in
/docsdirectory - Contact: support@pubflow.com
Built with β€οΈ by the Pubflow Team