A Ktor backend API for the Compose-Weatherify Android app, providing weather data, user authentication, and feedback management.
Weatherify API is a Kotlin-based backend service built with Ktor and MongoDB. It provides:
- Real-time weather and air pollution data
- User authentication with JWT
- Feedback submission and management
- Interactive API documentation UI
The application follows a modular architecture with clear separation of concerns. See the Architecture Diagram for a visual representation of the system components and their relationships.
The application implements several key data flows:
- Weather Data Retrieval - How weather data is fetched from external APIs and returned to clients
- Authentication - User registration and login processes
- Feedback Submission - How feedback is submitted and stored
Detailed sequence diagrams for these flows are available in the Data Flows Documentation.
The project includes the following documentation files:
- README.md: This file - provides an overview of the project, setup instructions, and basic usage information.
- API Reference: Comprehensive documentation of all API endpoints, request parameters, response formats, and authentication requirements.
- Architecture Diagram: Visual representation of the system components and their relationships.
- Data Flows Documentation: Detailed sequence diagrams for key data flows in the application.
- Database Indexes: Documentation of database indexes for query optimization, including index creation, management, and performance considerations.
The API provides endpoints for weather data, authentication, and feedback management. For detailed documentation of all endpoints, request parameters, response formats, and examples, see the API Reference.
GET /weather?lat={latitude}&lon={longitude}
Returns comprehensive weather data including current conditions, hourly and daily forecasts.
GET /air-pollution?lat={latitude}&lon={longitude}
Returns air quality information for the specified location.
POST /register
Registers a new user. Requires email and password in request body.
POST /login
Authenticates a user and returns a JWT token. Requires email and password in request body.
POST /logout
Logs out the current user by clearing the jwt_token cookie on the server and invalidating the session. Returns 200 with a JSON ApiResponse.
Example request:
POST /logout
Example success response:
{
"status": true,
"message": "Logged out successfully",
"data": null
}
Notes:
- Works for both admin and regular users.
- When integrating in the browser, also clear any locally stored JWT (e.g., localStorage) after calling this endpoint.
GET /feedback?id={feedbackId}
Retrieves feedback by ID.
POST /feedback
Submits new feedback. Requires deviceId, deviceOs, feedbackTitle, and feedbackDescription.
DELETE /feedback?id={feedbackId}
Deletes feedback by ID.
- JDK 17+
- MongoDB (local or remote)
- OpenWeatherMap API key (for weather data)
WEATHER_URL: Weather data API URLAIR_POLLUTION_URL: Air pollution data API URLDB_NAME: MongoDB database nameJWT_EXPIRATION: JWT token expiration time in millisecondsJWT_AUDIENCE: JWT audienceJWT_ISSUER: JWT issuerJWT_REALM: JWT realmWEATHER_API_KEY: OpenWeatherMap API key (for local development)DB_CONNECTION_STRING: MongoDB connection string (for local development)SENDGRID_API_KEY: SendGrid API key (optional, for email notifications)FROM_EMAIL: Email address to send from (optional, default: noreply@androidplay.com)FROM_NAME: Display name for email sender (optional, default: Androidplay)
See .env.example for a complete list of environment variables
and Email Configuration Guide for detailed email setup instructions.
# Build the project
./gradlew build
# Run the server
./gradlew runThe server will start at http://0.0.0.0:8080
The application is configured for deployment to Google Cloud Platform App Engine:
./gradlew appengineDeploysrc/
├── main/
│ ├── kotlin/
│ │ ├── Application.kt # Main application entry point
│ │ ├── Authentication.kt # JWT authentication configuration
│ │ ├── HTTP.kt # HTTP configuration
│ │ ├── Monitoring.kt # Logging and monitoring
│ │ ├── Routing.kt # API route configuration
│ │ ├── config/ # Environment and JWT configuration
│ │ ├── data/ # Data models and database access
│ │ ├── route/ # API route handlers
│ │ └── util/ # Utility classes and constants
│ └── resources/ # Static resources and configuration
└── test/ # Test classes
- Kotlin: Modern JVM language
- Ktor: Lightweight asynchronous web framework
- MongoDB: NoSQL database
- kotlinx.serialization: JSON serialization
- kotlinx.html: HTML DSL
- Google Cloud Platform: Hosting platform
See the full guide: docs/ci-cd.md
Quick start:
- Local build and tests: make build (or ./gradlew clean test jacocoTestReport shadowJar)
- Run locally: make run (runs build/libs/weatherify-api-all.jar on port 8080)
- Cloud Build (build only): gcloud builds submit --config=cloudbuild.yaml --substitutions=_DEPLOY=" false" .
- Cloud Build (build + deploy): gcloud builds submit --config=cloudbuild.yaml --substitutions=_ DEPLOY="true",_PROMOTE="false",_APP_YAML="src/main/appengine/app.yaml" .
- GitHub Actions: PRs and pushes run Build and Test workflow automatically; use “Deploy via Cloud Build” workflow manually for deployments.
Notes:
- Deploy workflow uses Workload Identity Federation. Add repository secrets: GCP_WORKLOAD_IDENTITY_PROVIDER, GCP_SERVICE_ACCOUNT_EMAIL, GCP_PROJECT_ID.
- Shadow JAR name is weatherify-api-all.jar, matching src/main/appengine/app.yaml entrypoint.
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
To make the project scalable and maintainable, route registration is modular.
- Each feature exposes a Ktor
Routeextension (e.g.,Route.weatherRoute()), keeping handlers self-contained. - A tiny
RouteRegistrarinterface allows features to self-register their routes without changing central wiring. - An ordered list of registrars is provided via Koin in
di/RouteModule.kt, andbase/Routing.ktiterates this list.
Add a new page/feature:
- Create or reuse a
Routeextension undersrc/main/kotlin/route/.... - Add a registrar in
route/common/Registrars.ktthat delegates to the extension, e.g.:object MyFeatureRegistrar : RouteRegistrar { override fun register(r: Route) { with(r) { myFeatureRoute() } } }
- Bind it in
di/RouteModule.ktand add it to the ordered list. - If your feature needs new services/repos, wire them in
di/DomainModule.kt/di/DataModule.kt.
Nothing in existing behavior changes when you follow this pattern; it simply reduces coupling.
For contribution guidelines, coding conventions, and how to add features safely, see CONTRIBUTING.md.