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=1 -
With 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_date
John,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.