# Flowfull Elixir Client
[](https://hex.pm/packages/flowfull)
[](https://elixir-lang.org/)
[](LICENSE)
A zero-dependency Elixir client library for Flowfull backends with built-in authentication, session management, and query building. Includes Phoenix integration for WebSockets and LiveView.
> **Professional, type-safe, and production-ready Elixir client for Flowfull API backends.**
## Features
- ✅ **Minimal Dependencies** - Only Req and Jason
- ✅ **Type-Safe** - Full typespec coverage
- ✅ **Session Management** - Auto-detection with pluggable storage
- ✅ **Query Builder** - Chainable API with 14+ filter operators
- ✅ **Authentication** - Complete auth system (25+ endpoints)
- ✅ **Retry Logic** - Configurable retries with exponential backoff
- ✅ **Interceptors** - Request/response middleware
- ✅ **Phoenix Integration** - WebSocket and LiveView support
- ✅ **Multi-Tenant** - Built-in multi-tenant support
- ✅ **File Upload** - Multipart form data support
## Installation
Add `flowfull` to your list of dependencies in `mix.exs`:
```elixir
def deps do
[
{:flowfull, "~> 0.1.0"}
]
end
```
## Quick Start
```elixir
# Create a client
client = Flowfull.new("https://api.example.com")
# Make a simple GET request
{:ok, response} = Flowfull.get(client, "/users")
# Use query builder
{:ok, users} =
Flowfull.query(client, "/users")
|> Flowfull.Query.where("age", Flowfull.Operators.gte(18))
|> Flowfull.Query.where("status", Flowfull.Operators.eq("active"))
|> Flowfull.Query.sort("created_at", :desc)
|> Flowfull.Query.page(1)
|> Flowfull.Query.limit(10)
|> Flowfull.Query.execute()
# Authentication
{:ok, result} = Flowfull.Auth.login(client, %{
email: "user@example.com",
password: "password123"
})
# Get current user
{:ok, user} = Flowfull.Auth.me(client)
```
## Configuration
```elixir
# With options
client = Flowfull.new("https://api.example.com",
session_id: "abc123",
timeout: 60_000,
headers: %{"X-Custom" => "value"},
retry_attempts: 5,
retry_exponential: true,
storage: Flowfull.Storage.File.new(".sessions")
)
# With session function
client = Flowfull.new("https://api.example.com",
get_session_id: fn ->
{:ok, System.get_env("SESSION_ID")}
end
)
# Include session in all requests
client = Flowfull.new("https://api.example.com",
include_session: true,
session_header: "X-Session-Id"
)
```
## HTTP Methods
```elixir
# GET
{:ok, response} = Flowfull.get(client, "/users")
# POST
{:ok, response} = Flowfull.post(client, "/users", %{
name: "John Doe",
email: "john@example.com"
})
# PUT
{:ok, response} = Flowfull.put(client, "/users/123", %{
name: "Jane Doe"
})
# PATCH
{:ok, response} = Flowfull.patch(client, "/users/123", %{
email: "jane@example.com"
})
# DELETE
{:ok, response} = Flowfull.delete(client, "/users/123")
```
## Query Builder
```elixir
# Complex query with multiple filters
{:ok, products} =
Flowfull.query(client, "/products")
|> Flowfull.Query.where("price", Flowfull.Operators.between(10, 100))
|> Flowfull.Query.where("category", Flowfull.Operators.in_list(["electronics", "books"]))
|> Flowfull.Query.where("name", Flowfull.Operators.ilike("%laptop%"))
|> Flowfull.Query.where("stock", Flowfull.Operators.gt(0))
|> Flowfull.Query.sort("price", :asc)
|> Flowfull.Query.sort("created_at", :desc)
|> Flowfull.Query.page(1)
|> Flowfull.Query.limit(20)
|> Flowfull.Query.select(["id", "name", "price"])
|> Flowfull.Query.execute()
```
## Filter Operators
| Operator | Function | Example | SQL Equivalent |
|----------|----------|---------|----------------|
| Equality | `eq(value)` | `where("age", eq(25))` | `age = 25` |
| Not Equal | `ne(value)` | `where("status", ne("inactive"))` | `status != 'inactive'` |
| Greater Than | `gt(value)` | `where("price", gt(100))` | `price > 100` |
| Greater or Equal | `gte(value)` | `where("age", gte(18))` | `age >= 18` |
| Less Than | `lt(value)` | `where("stock", lt(10))` | `stock < 10` |
| Less or Equal | `lte(value)` | `where("price", lte(50))` | `price <= 50` |
| LIKE | `like(pattern)` | `where("name", like("%John%"))` | `name LIKE '%John%'` |
| ILIKE | `ilike(pattern)` | `where("email", ilike("%@gmail.com"))` | `email ILIKE '%@gmail.com'` |
| Contains | `contains(value)` | `where("description", contains("laptop"))` | `description LIKE '%laptop%'` |
| Starts With | `starts_with(prefix)` | `where("name", starts_with("A"))` | `name LIKE 'A%'` |
| Ends With | `ends_with(suffix)` | `where("email", ends_with("@example.com"))` | `email LIKE '%@example.com'` |
| IN | `in_list(values)` | `where("status", in_list(["active", "pending"]))` | `status IN ('active', 'pending')` |
| NOT IN | `not_in(values)` | `where("role", not_in(["admin", "super"]))` | `role NOT IN ('admin', 'super')` |
| IS NULL | `is_null()` | `where("deleted_at", is_null())` | `deleted_at IS NULL` |
| NOT NULL | `not_null()` | `where("email", not_null())` | `email IS NOT NULL` |
| BETWEEN | `between(min, max)` | `where("age", between(18, 65))` | `age BETWEEN 18 AND 65` |
| NOT BETWEEN | `not_between(min, max)` | `where("price", not_between(0, 10))` | `price NOT BETWEEN 0 AND 10` |
## Authentication
### Login & Registration
```elixir
# Login with email
{:ok, result} = Flowfull.Auth.login(client, %{
email: "user@example.com",
password: "password123"
})
# Login with username
{:ok, result} = Flowfull.Auth.login(client, %{
user_name: "johndoe",
password: "password123"
})
# Register new user
{:ok, result} = Flowfull.Auth.register(client, %{
email: "user@example.com",
password: "password123",
name: "John",
last_name: "Doe"
})
# Logout
{:ok, _} = Flowfull.Auth.logout(client)
# Get current user
{:ok, user} = Flowfull.Auth.me(client)
# Update profile
{:ok, user} = Flowfull.Auth.update_profile(client, %{
name: "Jane",
phone: "+1234567890"
})
```
### Password Reset
```elixir
# Request password reset
{:ok, _} = Flowfull.Auth.Password.request_reset(client, %{
email: "user@example.com",
reset_url: "https://app.example.com/reset-password"
})
# Validate reset token
{:ok, result} = Flowfull.Auth.Password.validate_token(client, token)
# Complete password reset
{:ok, _} = Flowfull.Auth.Password.complete_reset(client, %{
token: token,
password: "newpassword123"
})
# Change password (authenticated)
{:ok, _} = Flowfull.Auth.Password.change_password(client, %{
old_password: "oldpassword",
new_password: "newpassword"
})
```
### Token Authentication
```elixir
# Create login token
{:ok, result} = Flowfull.Auth.Token.create(client, %{
email: "user@example.com",
token_url: "https://app.example.com/login"
})
# Validate token
{:ok, result} = Flowfull.Auth.Token.validate(client, token)
# Login with token
{:ok, result} = Flowfull.Auth.Token.login(client, token)
```
### Social OAuth
```elixir
# Get available providers
{:ok, providers} = Flowfull.Auth.Social.get_providers(client)
# Start OAuth flow
{:ok, auth_url} = Flowfull.Auth.Social.start_oauth(
client,
:google,
"https://app.example.com/callback"
)
# Login with OAuth (after callback)
{:ok, result} = Flowfull.Auth.Social.login_api(client, %{
provider: "google",
code: oauth_code
})
# Get linked accounts
{:ok, accounts} = Flowfull.Auth.Social.get_accounts(client)
# Unlink account
{:ok, _} = Flowfull.Auth.Social.unlink(client, :google)
```
## Phoenix Integration
### WebSocket Authentication
```elixir
# In your UserSocket module
defmodule MyAppWeb.UserSocket do
use Phoenix.Socket
def connect(%{"session_id" => session_id}, socket, _connect_info) do
client = Flowfull.new(Application.get_env(:my_app, :flowfull_url))
case Flowfull.Phoenix.validate_socket_session(client, session_id) do
{:ok, user} ->
{:ok, assign(socket, :user, user)}
{:error, _reason} ->
:error
end
end
def connect(_params, _socket, _connect_info), do: :error
end
```
### LiveView Authentication
```elixir
# In your LiveView module
defmodule MyAppWeb.DashboardLive do
use MyAppWeb, :live_view
def mount(_params, %{"session_id" => session_id}, socket) do
client = Flowfull.new(Application.get_env(:my_app, :flowfull_url))
case Flowfull.Phoenix.validate_liveview_session(client, session_id) do
{:ok, user} ->
{:ok, assign(socket, :current_user, user)}
{:error, _reason} ->
{:ok, redirect(socket, to: "/login")}
end
end
end
```
### Controller Authentication Plug
```elixir
# In your router
pipeline :authenticated do
plug Flowfull.Phoenix.SessionPlug,
client: fn -> Flowfull.new(Application.get_env(:my_app, :flowfull_url)) end
end
scope "/dashboard", MyAppWeb do
pipe_through [:browser, :authenticated]
get "/", DashboardController, :index
end
```
## Storage Adapters
### Memory Storage (Agent-based)
```elixir
# Start the memory storage
{:ok, _} = Flowfull.Storage.Memory.start_link()
# Use with client
client = Flowfull.new("https://api.example.com",
storage: Flowfull.Storage.Memory
)
```
### File Storage
```elixir
# Use file storage
client = Flowfull.new("https://api.example.com",
storage: Flowfull.Storage.File.new(".sessions")
)
```
### ETS Storage (High Performance)
```elixir
# Initialize ETS storage
Flowfull.Storage.ETS.init()
# Use with client
client = Flowfull.new("https://api.example.com",
storage: Flowfull.Storage.ETS
)
```
## Interceptors
```elixir
# Add request interceptor
client = Flowfull.add_request_interceptor(client, fn request ->
IO.inspect(request, label: "Request")
{:ok, request}
end)
# Add response interceptor
client = Flowfull.add_response_interceptor(client, fn response ->
IO.inspect(response, label: "Response")
{:ok, response}
end)
```
## Examples
See the [examples](./examples) directory for complete examples:
- [Basic Usage](./examples/basic.exs) - Simple HTTP requests
- [Query Builder](./examples/query.exs) - Advanced queries
- [Authentication](./examples/auth.exs) - Complete auth flow
- [Phoenix WebSocket](./examples/phoenix_socket.exs) - WebSocket integration
- [Phoenix LiveView](./examples/phoenix_liveview.exs) - LiveView integration
## Documentation
- [Quick Start Guide](QUICKSTART.md) - Get started in 5 minutes
- [API Documentation](https://hexdocs.pm/flowfull) - Complete API reference
- [Phoenix Integration Guide](docs/PHOENIX.md) - Phoenix-specific features
## Contributing
Contributions are welcome! Please read our [Contributing Guide](CONTRIBUTING.md) for details.
## License
**Flowfull Elixir Client** is licensed under the **AGPL-3.0-only** license.
### What does this mean?
- ✅ **Free to use** - Use commercially without paying anything
- ✅ **Modify freely** - Change the code as needed
- ✅ **Distribute** - Share with others
- ⚠️ **Share modifications** - If you modify and offer as a web service, you must release your changes
- ⚠️ **Same license** - Derivative works must use AGPL-3.0
### Commercial License Available
For organizations that cannot comply with AGPL-3.0 or need:
- 💼 Keep modifications private
- 🛡️ Legal indemnification
- 🎯 Enterprise support and SLA
- 🚀 Custom features
**Contact:** enterprise@pubflow.com
**Learn more:** https://pubflow.com/dual-licensing
See [LICENSE](LICENSE) for full details.
## Support
- 📖 [Documentation](https://hexdocs.pm/flowfull)
- 💬 [Issues](https://github.com/pubflow/flowfull-elixir/issues)
- 🌟 [GitHub](https://github.com/pubflow/flowfull-elixir)
- 💼 [Commercial License](https://pubflow.com/dual-licensing)
---
**Copyright © 2024-present Pubflow, Inc.**
**SPDX-License-Identifier:** AGPL-3.0-only
Made with ❤️ by the Pubflow team