A REST API for managing church members, households, groups, services, and attendance. Built with Rust, Axum, and PostgreSQL.
- Framework: Axum 0.7
- Database: PostgreSQL 16
- ORM: SQLx (compile-time checked queries)
- Authentication: JWT with refresh tokens
- Password Hashing: Argon2
cms-api/
├── src/
│ ├── main.rs # Entry point
│ ├── lib.rs # Module exports, AppState
│ ├── config.rs # Environment configuration
│ ├── errors.rs # Unified error handling
│ ├── routes/ # HTTP handlers
│ │ ├── auth.rs # Login, refresh, logout
│ │ ├── users.rs # Admin user management
│ │ ├── members.rs # Church member CRUD + CSV import
│ │ ├── households.rs # Household CRUD + member linking
│ │ ├── groups.rs # Groups/ministries CRUD
│ │ ├── services.rs # Church services CRUD
│ │ └── attendance.rs # Check-in, bulk check-in
│ ├── services/ # Business logic layer
│ ├── repositories/ # Database queries
│ ├── models/ # Database models
│ ├── dto/ # Request/Response types
│ └── middleware/ # Auth extractors
├── migrations/ # SQLx database migrations
├── tests/ # Integration tests
├── Dockerfile # Production build
├── Dockerfile.dev # Development with hot reload
├── docker-compose.yml # Production setup
├── docker-compose.dev.yml # Development setup
└── docker-compose.test.yml # Test setup
- Users: Admin accounts with roles (super_admin, admin, staff)
- Members: Church members with soft delete
- Households: Family groupings with member linking
- Groups: Ministries, committees, cell groups (configurable type)
- Services: Church services/events
- Attendance: Check-in tracking with bulk support
- JWT access tokens (configurable expiration)
- Refresh token rotation
- Role-based access control
- Password hashing with Argon2
- CSV bulk import for members
- Pagination on all list endpoints
- Search/filter support
- Soft delete for members
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/v1/auth/login | Login, returns tokens |
| POST | /api/v1/auth/refresh | Refresh access token |
| POST | /api/v1/auth/logout | Revoke refresh token |
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/v1/users | List users |
| POST | /api/v1/users | Create user |
| GET | /api/v1/users/:id | Get user |
| PATCH | /api/v1/users/:id | Update user |
| DELETE | /api/v1/users/:id | Delete user |
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/v1/members | List members (paginated) |
| POST | /api/v1/members | Create member |
| POST | /api/v1/members/import | Bulk import from CSV |
| GET | /api/v1/members/:id | Get member |
| PATCH | /api/v1/members/:id | Update member |
| DELETE | /api/v1/members/:id | Soft delete member |
| GET | /api/v1/members/:id/attendance | Member's attendance |
| GET | /api/v1/members/:id/groups | Member's groups |
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/v1/households | List households |
| POST | /api/v1/households | Create household |
| GET | /api/v1/households/:id | Get household |
| PATCH | /api/v1/households/:id | Update household |
| DELETE | /api/v1/households/:id | Delete household |
| GET | /api/v1/households/:id/members | List members |
| PUT | /api/v1/households/:id/members/:mid | Link member |
| DELETE | /api/v1/households/:id/members/:mid | Unlink member |
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/v1/groups | List groups |
| POST | /api/v1/groups | Create group |
| GET | /api/v1/groups/:id | Get group |
| PATCH | /api/v1/groups/:id | Update group |
| DELETE | /api/v1/groups/:id | Delete group |
| GET | /api/v1/groups/:id/members | List members |
| POST | /api/v1/groups/:id/members/:mid | Add member |
| DELETE | /api/v1/groups/:id/members/:mid | Remove member |
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/v1/services | List services |
| POST | /api/v1/services | Create service |
| GET | /api/v1/services/:id | Get service |
| PATCH | /api/v1/services/:id | Update service |
| DELETE | /api/v1/services/:id | Delete service |
| GET | /api/v1/services/:id/attendance | Service attendance |
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/v1/attendance | Check in member |
| POST | /api/v1/attendance/bulk | Bulk check in |
| DELETE | /api/v1/attendance/:id | Remove record |
- Rust 1.83+
- PostgreSQL 16+ (or Docker)
Clone and setup:
cd cms-api cp .env.example .env # Edit .env with your database credentials
Create database:
createdb cms_api
Run the server:
cargo run
Start everything:
docker compose up --build
With hot reload:
docker compose -f docker-compose.dev.yml up --build
Just the database:
docker compose up db cargo run
Create test database:
createdb cms_api_test # Or with Docker: docker exec -it <container> psql -U postgres -c "CREATE DATABASE cms_api_test"
Run tests (sequential for DB tests):
cargo test -- --test-threads=1With output:
cargo test -- --test-threads=1 --nocapture
| Variable | Description | Default |
|---|---|---|
DATABASE_URL | PostgreSQL connection string | Required |
JWT_SECRET | Secret key for JWT signing | Required |
JWT_EXPIRATION_HOURS | Access token lifetime | 24 |
REFRESH_TOKEN_EXPIRATION_DAYS | Refresh token lifetime | 7 |
SERVER_HOST | Server bind address | 0.0.0.0 |
SERVER_PORT | Server port | 3000 |
RUST_LOG | Log level | cms_api=debug |
For bulk member import, use this CSV format:
first_name,last_name,email,phone,date_of_birth,gender,address,membership_status,membership_dateJohn,Smith,john@example.com,555-0100,1985-03-15,male,"123 Main St",active,2020-01-01See sample_members.csv for a complete example.
The database includes these tables:
users- Admin accountshouseholds- Family groupingsmembers- Church members (with soft delete)groups- Ministries, committees, etc.member_groups- Many-to-many relationshipservices- Church services/eventsattendance- Service attendance recordsrefresh_tokens- JWT refresh token storage
Migrations are in the migrations/ directory and run automatically on startup.
Private project - All rights reserved.