13 releases
| 0.2.8 | Feb 8, 2026 |
|---|---|
| 0.2.7 | Feb 8, 2026 |
| 0.2.3 | Jan 31, 2026 |
| 0.1.3 | Jan 29, 2026 |
#1325 in Database interfaces
200KB
3.5K
SLoC
Brylix
A Rust framework for building GraphQL APIs on AWS Lambda with SeaORM and multi-tenant support.
Features
- GraphQL API - Built on async-graphql with playground support
- AWS Lambda - Optimized for serverless deployment with cargo-lambda
- SeaORM - Type-safe database operations with MySQL/PostgreSQL support
- Multi-tenant - Pool-per-droplet architecture for SaaS applications
- JWT Authentication - Secure token-based authentication with multi-role support
- Validation - Built-in input validation utilities
- Email Provider - SMTP email support with attachments
- S3 Provider - Presigned URL generation for file uploads/downloads
- Pagination - Generic pagination utilities for GraphQL connections
- Helpers - JSON parsing, timestamps, soft delete, and ID parsing utilities
Installation
[dependencies]
brylix = "0.2"
Quick Start
use brylix::prelude::*;
use async_graphql::{EmptySubscription, Schema};
#[tokio::main]
async fn main() -> Result<(), brylix::Error> {
Brylix::builder()
.config_from_env()
.with_jwt_auth()
.with_migrations::<migration::Migrator>()
.build_schema(|| {
Schema::build(
graphql::Query,
graphql::Mutation,
EmptySubscription,
)
.finish()
})
.build()?
.run()
.await
}
Feature Flags
| Feature | Description | Default |
|---|---|---|
mysql |
MySQL/MariaDB support via sqlx | Yes |
postgres |
PostgreSQL support via sqlx | No |
playground |
GraphQL Playground IDE | Yes |
multi-tenant |
Multi-tenant support | No |
email |
SMTP email with attachments | No |
s3 |
S3 presigned URLs for file uploads | No |
admin-override |
Temporary admin elevation for POS/kiosk | No |
full |
All features enabled | No |
# PostgreSQL instead of MySQL
brylix = { version = "0.2", default-features = false, features = ["postgres", "playground"] }
# Multi-tenant support
brylix = { version = "0.2", features = ["multi-tenant"] }
# Email support
brylix = { version = "0.2", features = ["email"] }
# S3 presigned URLs
brylix = { version = "0.2", features = ["s3"] }
# Admin override (POS/kiosk temporary admin elevation)
brylix = { version = "0.2", features = ["admin-override"] }
Utilities
Pagination
use brylix::prelude::*;
// In a resolver:
async fn list_users(ctx: &Context<'_>, page: u64, per_page: u64) -> Result<Connection<UserDto>> {
let data = ctx.data_unchecked::<ContextData>();
let (items, total) = UserService::paginated(&data.db, page, per_page).await?;
Ok(Connection::new(items, total, page, per_page))
}
// Or use IntoConnection trait with (Vec<T>, u64) tuples:
let result: (Vec<UserDto>, u64) = (items, total);
let connection = result.into_connection(page, per_page);
GraphQL ID Parsing
use brylix::prelude::*;
// Parse string IDs to i64
let user_id = parse_gql_id("123")?; // Ok(123)
let id = parse_gql_id_field("42", "user_id")?; // Custom error field name
// Or use the macro
let id = gql_id!("123");
let id = gql_id!("123", "user_id");
JSON Column Helpers
use brylix::prelude::*;
use serde_json::json;
// Parse JSON database columns into typed structs
let json_col: Option<serde_json::Value> = Some(json!({"key": "value"}));
let parsed: Option<MyStruct> = json_col.parse_as();
let with_default: Vec<String> = json_col.parse_or_default();
Timestamp Helpers
use brylix::prelude::*;
let now = utc_now(); // chrono::DateTime<Utc>
// Implement Timestamped for your ActiveModel:
impl Timestamped for users::ActiveModel {
fn set_created_at(&mut self) { self.created_at = Set(utc_now()); }
fn set_updated_at(&mut self) { self.updated_at = Set(utc_now()); }
}
let mut model = users::ActiveModel { .. };
model.set_timestamps(); // Sets both created_at and updated_at
Soft Delete
use brylix::prelude::*;
// Use status constants
let active = status::ACTIVE; // "active"
let deleted = status::DELETED; // "deleted"
// Implement SoftDeletable for your models
impl SoftDeletable for Post {
fn mark_deleted(&mut self) { self.status = status::DELETED.to_string(); }
fn is_deleted(&self) -> bool { self.status == status::DELETED }
}
Multi-Role Authentication
use brylix::prelude::*;
// Configure multiple JWT secrets
let jwt_config = MultiRoleJwtConfig::new()
.add_role("user", std::env::var("JWT_SECRET").unwrap())
.add_role("admin", std::env::var("ADMIN_JWT_SECRET").unwrap());
// Validate token to determine role
let role = jwt_config.validate(token); // Option<AuthRole>
// Set role in context
let ctx = ContextData::single_tenant(db, user_id, Some(AuthRole::Admin(1)));
// Use guards in resolvers
let admin_id = require_admin(ctx)?; // Errors if not admin
let role = get_auth_role(ctx); // Option<&AuthRole>
Admin Override (POS/Kiosk Pattern)
Allows a logged-in user (e.g. cashier) to perform admin-only actions when an admin "taps in"
with a short-lived override token. Requires the admin-override feature.
use brylix::prelude::*;
// 1. After verifying admin credentials, issue a short-lived token
let config = AdminOverrideConfig::new(std::env::var("ADMIN_JWT_SECRET").unwrap())
.with_expiry_secs(60); // 60 seconds default
let token = issue_admin_override_token(&config, admin_id, "Admin Name", Some("delete_invoice"))?;
// 2. Frontend sends both headers for the privileged action:
// Authorization: Bearer <cashier_token>
// X-Admin-Override: <admin_override_token>
// 3. In resolvers, require_admin() works for BOTH scenarios:
async fn delete_invoice(ctx: &Context<'_>, id: i64) -> Result<bool> {
let admin_id = require_admin(ctx)?; // Works for direct admin OR override
let user_id = require_auth_user_id(ctx)?; // Still the cashier
// For audit trail:
if let Some(ao) = get_admin_override(ctx) {
let audit = AdminOverrideAudit {
actor_user_id: user_id,
authorizer_admin_id: ao.admin_id,
authorizer_name: ao.admin_name.clone(),
action: ao.action.clone(),
};
audit.log(); // Logs via tracing
}
Ok(true)
}
// 4. Or require BOTH user auth + admin override explicitly:
let (cashier_id, admin_override) = require_auth_with_admin_override(ctx)?;
Environment Variables
# Required
DATABASE_URL=mysql://user:password@host/database
JWT_SECRET=your-secret-key
JWT_EXP_DAYS=7
# Email (optional, requires `email` feature)
SMTP_HOST=smtp.example.com
SMTP_PORT=465
SMTP_USER=your-email@example.com
SMTP_PASSWORD=your-password
SMTP_FROM_NAME=Your App Name
SMTP_FROM_EMAIL=noreply@example.com
# S3 (optional, requires `s3` feature)
S3_BUCKET=my-bucket-name
S3_REGION=us-east-1
S3_UPLOAD_EXPIRES_SECS=3600
S3_DOWNLOAD_EXPIRES_SECS=3600
# Custom credentials for local development (optional)
# If not set, falls back to default AWS credential chain (IAM role for Lambda)
S3_ACCESS_KEY_ID=your-access-key
S3_SECRET_ACCESS_KEY=your-secret-key
# Admin Override (optional, requires `admin-override` feature)
ADMIN_JWT_SECRET=your-admin-secret # Same secret used for admin role JWT
ADMIN_OVERRIDE_EXPIRY_SECS=60 # Optional, default 60 seconds
Documentation
Full documentation is available at docs.rs/brylix.
License
Licensed under either of Apache License 2.0 or MIT License at your option.
Dependencies
~46–76MB
~1M SLoC