Flight Price API
Project Overview
Flight Price API is a high-performance microservice built in Go that aggregates flight pricing data from multiple instances of the same provider, Google Flights.
I didn't have access to paid versions of private airline APIs or a paid RapidAPI subscription. I consider the main challenge of this project to be designing a system that efficiently handles multiple concurrent calls in the background.
Therefore, I implemented only one provider (Google Flights) and ran three instances of it, enabling multiple concurrent API calls—although the data returned is the same across instances.
Finding and accessing the necessary data was time-consuming, and I don't believe that was the primary purpose of the project. As feedback for Jobsity, it might be helpful to provide endpoints with pre-loaded data for these types of challenges, allowing candidates to focus on development rather than data acquisition.
The service implements concurrent API requests to ensure quick response times and provides structured comparison data including cheapest and fastest flight options.
Key Features
- Multi-provider Integration: Fetches flight data from multiple sources concurrently
- Price Comparison: Identifies the cheapest and fastest flights across all providers
- Secure API: JWT authentication for all API endpoints
- Concurrent Processing: Uses Go's goroutines and channels for parallel requests
- Docker Support: Containerized deployment with optimized Docker configuration
- Web Interface: Simple frontend for searching and comparing flights
Architecture & Design
High-level Architecture

Component Breakdown
-
API Layer:
- Handles HTTP requests and responses using Gin framework
- Implements authentication middleware for JWT validation
- Routes requests to appropriate handlers
-
Service Layer:
- Manages business logic for flight searches
- Coordinates concurrent requests to multiple providers
- Aggregates and processes results from all providers
-
Provider Layer:
- Abstracts external API interactions behind a common interface
- Handles mapping between external API responses and internal models
- Implements provider-specific error handling
-
Auth System:
- JWT-based authentication for API security
- Token generation and validation
Concurrent Request Model
The service uses Go's concurrency primitives to fetch flight data from multiple providers simultaneously:
- A search request triggers concurrent goroutines for each provider
- Each goroutine makes an API request to its assigned provider
- Results are collected through a channel with proper timeout handling
- Data from all providers is aggregated, with partial results returned if some providers fail
for _, provider := range s.providers {
p := flightProvider{
FlightProvider: provider,
resultCh: resultCh,
}
go p.searchFlights(ctx, req, s.searchTimeout)
}
for remainingProviders > 0 {
select {
case <-ctx.Done():
return nil, ctx.Err()
case <-timer.C:
case result := <-resultCh:
}
}
Data Flow
- Client submits search parameters (origin, destination, date)
- Request is authenticated via JWT middleware
- Flight service dispatches concurrent requests to all providers
- Results are collected, processed, and compared
- Structured response with cheapest/fastest options is returned to client
API Endpoints
Authentication
POST /login
Authenticates a user and returns a JWT token.
Request:
{
"username": "username",
"password": "password"
}
Response:
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
User Information
GET /api/user
Returns information about the authenticated user.
Headers:
Authorization: Bearer <token>
Response:
{
"username": "username"
}
Flight Search
GET /api/flights/search?origin=XXX&destination=YYY&date=YYYY-MM-DD
Searches for flights matching the specified criteria.
Headers:
Authorization: Bearer <token>
Parameters:
origin
: Origin airport code (e.g., LAX, AUS)
destination
: Destination airport code (e.g., JFK, ORD)
date
: Flight date in YYYY-MM-DD format
Response:
{
"count": 3,
"flights": [
{
"cheapest": {
"Airline": "Delta Air Lines",
"FlightNumber": "DL1234",
"Origin": "LAX",
"Destination": "JFK",
"DepartureTime": "2025-03-19T06:00:00Z",
"ArrivalTime": "2025-03-19T14:15:00Z",
"Duration": 29700000000000,
"Price": 299.99,
"Provider": "Google Flights 1",
"Stops": 0
},
"fastest": {
"Airline": "American Airlines",
"FlightNumber": "AA789",
"Origin": "LAX",
"Destination": "JFK",
"DepartureTime": "2025-03-19T08:30:00Z",
"ArrivalTime": "2025-03-19T16:30:00Z",
"Duration": 28800000000000,
"Price": 349.99,
"Provider": "Google Flights 1",
"Stops": 0
}
},
]
}
Authentication & Security
JWT Implementation
The service uses JSON Web Tokens (JWT) for authentication:
-
Token Generation:
- Generated upon successful login
- Contains user identifier in claims
- Configurable expiration time (default: 24 hours)
-
Token Validation:
- All API requests must include a valid token in the Authorization header
- Auth middleware extracts and validates tokens before processing requests
- Invalid or expired tokens result in 401 Unauthorized responses
-
Security Best Practices:
- Token secret configured via environment variables
- Non-sensitive user info stored in token claims
- Token expiration enforced to limit exposure
Code Example: JWT Validation
authHeader := c.GetHeader("Authorization")
parts := strings.Split(authHeader, " ")
tokenString := parts[1]
claims, err := validator.Validate(secretKey, tokenString)
if err != nil {
c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid or expired token"})
c.Abort()
return
}
c.Set("userID", claims.UserID)
Parallel Processing
Concurrent API Requests
The service implements a pattern for making concurrent API requests to multiple flight providers:
- Each provider search runs in its own goroutine
- Results and errors are communicated through a channel
- A timeout mechanism ensures the service remains responsive
- The main goroutine collects and aggregates results
Error Handling
The service is designed to gracefully handle provider failures:
- If all providers fail, a comprehensive error is returned
- If some providers fail but others succeed, available results are returned with errors logged
- If a global timeout occurs, partial results are returned when available
Aggregation Logic
Flight data from all providers is processed to identify:
- The cheapest flight across all providers
- The fastest flight across all providers
- Provider-specific cheapest and fastest flights
Testing
Unit Tests
The codebase includes comprehensive unit tests for key components:
- Handler Tests: Verify API endpoint behavior
- Service Tests: Ensure business logic correctly processes flight data
- Provider Tests: Mock external API interactions to validate mapping logic
HTTPS/TLS Configuration for Production
Direct TLS in the Go application, modify the server startup code:
import "crypto/tls"
func main() {
certFile := os.Getenv("TLS_CERT_FILE")
keyFile := os.Getenv("TLS_KEY_FILE")
if certFile != "" && keyFile != "" {
log.Infof("Starting server with TLS on port %s", cfg.Server.Port)
r.RunTLS(":"+cfg.Server.Port, certFile, keyFile)
} else {
log.Infof("Starting server without TLS on port %s", cfg.Server.Port)
r.Run(":"+cfg.Server.Port)
}
}
Certificate Management
-
Development: For local development, use self-signed certificates
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout ./certs/server.key -out ./certs/server.crt
-
Production:
- With Nginx: Use Certbot
- With Traefik: Built-in ACME support
- Standalone: Use certbot in standalone mode to generate certificates
Security Recommendations
- Enforce TLS 1.2+: Disable older TLS/SSL protocols
- Implement HSTS: Add Strict-Transport-Security header
- Configure Strong Ciphers: Use modern, secure cipher suites
- Certificate Rotation: Set up automated certificate renewal
- Content Security Policy: Add CSP headers for the web interface
Running the Application
Docker Setup
Build and run the application using Docker:
make build
make start
make logs
make stop
Environment Variables
Configure the application through environment variables in the .env
file:
PORT | Server port | 8080 |
JWT_SECRET | Secret key for JWT signing | Required |
JWT_EXPIRES_IN | Token expiration in hours | 24 |
USERNAME | Default username | jobsity |
PASSWORD | Default password | 123456 |
RAPID_API_KEY | API key for RapidAPI | Required |
LOGGER_LOG_LEVEL | Log level (debug, info, warn, error) | debug |
Example API Requests
Set your RAPID_API_KEY
in the .env
file before trying the examples otherwise the flight search request will fail.
Login
curl -X POST http://localhost:8080/login \
-H "Content-Type: application/json" \
-d '{"username":"jobsity","password":"123456"}'
Search Flights
curl -X GET "http://localhost:8080/api/flights/search?origin=LAX&destination=JFK&date=2025-03-19" \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
Web Interface
The application includes a simple web interface for searching flights:
- Navigate to
http://localhost:8080
in your browser
- Log in with the configured credentials
- Use the search form to find flights
- View results including cheapest and fastest options
Troubleshooting
Common issues and solutions:
- API Key Error: Ensure your
RAPID_API_KEY
is valid and has access to the Google Flights API
- Authentication Failed: Verify username/password match the environment variables
- No Flight Results: Check that the origin/destination pair is supported (currently LAX→JFK and AUS→ORD)