HNG12 STAGE 1: NUMBER CLASSIFICATION API DEVELOPMENT & DEPLOYMENT
INTRODUCTION The task required the development of an API that classifies numbers based on mathematical properties and provides a fun fact retrieved from an external API. The API must return data in JSON format, be deployed publicly, and adhere to quality standards. The acceptance criteria ensure the API is functional, well-documented, and efficiently deployed. I then proceeded to update the package list and installed necessary system tools to ensure a smooth development environment. I ran the following commands: *sudo apt update && sudo apt upgrade -y sudo apt install python3 python3-pip git curl -y * This ensured that Python, Pip, Git, and Curl were available for use. SETTING UP THE PROJECT DIRECTORY I created a dedicated directory for the project and navigated into it: mkdir number_api && cd number_api Having created the directory I installed FastAPI and Uvicorn, which will serve as the API framework and ASGI server respectively: pip install fastapi uvicorn requests This step ensures that the necessary libraries are available for developing and running the API. WRITING THE API CODE Using a text editor, I created and edited main.py: nano main.py I followed that up with the following code, which defines the API functionality: **from fastapi import FastAPI, Query, HTTPException, Request from fastapi.responses import JSONResponse import requests from typing import List from fastapi.middleware.cors import CORSMiddleware app = FastAPI() CORS configuration origins = [ "http://localhost", "http://localhost:8080", "", # Allows requests from any origin (USE WITH CAUTION IN PRODUCTION) ] app.add_middleware( CORSMiddleware, allow_origins=origins, allow_credentials=True, allow_methods=[""], allow_headers=[""], ) def is_prime(n: int) -> bool: """Check if a number is prime.""" if n < 2: return False for i in range(2, int(abs(n) * 0.5) + 1): if n % i == 0: return False return True def is_armstrong(n: int) -> bool: """Check if a number is an Armstrong number.""" num_str = str(abs(n)) digits = [int(d) for d in num_str] power = len(digits) return sum(d ** power for d in digits) == abs(n) def digit_sum(n: int) -> int: """Calculate the sum of the digits of a number.""" return sum(int(d) for d in str(abs(n))) def get_fun_fact(n: int) -> str: """Fetch a fun fact about a number from the Numbers API.""" try: response = requests.get(f"http://numbersapi.com/{n}/math") response.raise_for_status() return response.text except requests.exceptions.RequestException: return "No fun fact available." @app.exception_handler(ValueError) async def value_error_handler(request: Request, exc: ValueError): return JSONResponse( status_code=400, content={"number": request.query_params.get("number", ""), "error": True}, ) @app.get("/api/classify-number") def classify_number(number: str = Query(..., description="Enter a number")): """ API endpoint to classify a number. Accepts numbers in different formats (negative, string, float, etc.). """ original_input = number # Store the exact user input try: # Convert input to an integer, even if it's a float or string parsed_number = int(float(number)) except ValueError: raise ValueError("Invalid input") is_negative = parsed_number < 0 abs_number = abs(parsed_number) properties: List[str] = [] is_armstrong_num = is_armstrong(abs_number) is_odd = abs_number % 2 != 0 if is_armstrong_num and is_odd: properties = ["armstrong", "odd"] elif is_armstrong_num and not is_odd: properties = ["armstrong", "even"] elif is_odd: properties = ["odd"] else: properties = ["even"] return { "number": parsed_number, "is_prime": is_prime(abs_number), "is_perfect": False, # No perfect number check in this version "properties": properties, "digit_sum": digit_sum(abs_number), "fun_fact": get_fun_fact(abs_number), } Run the app with Uvicorn if name == "main": import uvicorn uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=True)** This code processes a number, classifies its properties, and fetches a fun fact. RUNNING AND TESTING THE API LOCALLY To start the FastAPI server, I ran the following command: ** uvicorn main:app --host 0.0.0.0 --port 8000** I tested the API using a browser by navigating to: http://127.0.0.1:8000/docs The test was successful, indicated by the output below: SETTING UP GITHUB I initialized a Git repository, committed my files, and pushed them to GitHub: git init git add . git commit -m "Initial commit" git branch -M main git remote add origin https://github.com/OmachokoYakubu/HNG-12-DevOps-Projects git push -u origin main This ensures my code is version-controlled and publicly available. T
INTRODUCTION
The task required the development of an API that classifies numbers based on mathematical properties and provides a fun fact retrieved from an external API. The API must return data in JSON format, be deployed publicly, and adhere to quality standards. The acceptance criteria ensure the API is functional, well-documented, and efficiently deployed.
I then proceeded to update the package list and installed necessary system tools to ensure a smooth development environment. I ran the following commands:
*sudo apt update && sudo apt upgrade -y
sudo apt install python3 python3-pip git curl -y
*
This ensured that Python, Pip, Git, and Curl were available for use.
SETTING UP THE PROJECT DIRECTORY
I created a dedicated directory for the project and navigated into it:
mkdir number_api && cd number_api
Having created the directory I installed FastAPI and Uvicorn, which will serve as the API framework and ASGI server respectively:
pip install fastapi uvicorn requests
This step ensures that the necessary libraries are available for developing and running the API.
WRITING THE API CODE
Using a text editor, I created and edited main.py:
nano main.py
I followed that up with the following code, which defines the API functionality:
**from fastapi import FastAPI, Query, HTTPException, Request
from fastapi.responses import JSONResponse
import requests
from typing import List
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI()
CORS configuration
origins = [
"http://localhost",
"http://localhost:8080",
"", # Allows requests from any origin (USE WITH CAUTION IN PRODUCTION)
]
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=[""],
allow_headers=[""],
)
def is_prime(n: int) -> bool:
"""Check if a number is prime."""
if n < 2:
return False
for i in range(2, int(abs(n) * 0.5) + 1):
if n % i == 0:
return False
return True
def is_armstrong(n: int) -> bool:
"""Check if a number is an Armstrong number."""
num_str = str(abs(n))
digits = [int(d) for d in num_str]
power = len(digits)
return sum(d ** power for d in digits) == abs(n)
def digit_sum(n: int) -> int:
"""Calculate the sum of the digits of a number."""
return sum(int(d) for d in str(abs(n)))
def get_fun_fact(n: int) -> str:
"""Fetch a fun fact about a number from the Numbers API."""
try:
response = requests.get(f"http://numbersapi.com/{n}/math")
response.raise_for_status()
return response.text
except requests.exceptions.RequestException:
return "No fun fact available."
@app.exception_handler(ValueError)
async def value_error_handler(request: Request, exc: ValueError):
return JSONResponse(
status_code=400,
content={"number": request.query_params.get("number", ""), "error": True},
)
@app.get("/api/classify-number")
def classify_number(number: str = Query(..., description="Enter a number")):
"""
API endpoint to classify a number.
Accepts numbers in different formats (negative, string, float, etc.).
"""
original_input = number # Store the exact user input
try:
# Convert input to an integer, even if it's a float or string
parsed_number = int(float(number))
except ValueError:
raise ValueError("Invalid input")
is_negative = parsed_number < 0
abs_number = abs(parsed_number)
properties: List[str] = []
is_armstrong_num = is_armstrong(abs_number)
is_odd = abs_number % 2 != 0
if is_armstrong_num and is_odd:
properties = ["armstrong", "odd"]
elif is_armstrong_num and not is_odd:
properties = ["armstrong", "even"]
elif is_odd:
properties = ["odd"]
else:
properties = ["even"]
return {
"number": parsed_number,
"is_prime": is_prime(abs_number),
"is_perfect": False, # No perfect number check in this version
"properties": properties,
"digit_sum": digit_sum(abs_number),
"fun_fact": get_fun_fact(abs_number),
}
Run the app with Uvicorn
if name == "main":
import uvicorn
uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=True)**
This code processes a number, classifies its properties, and fetches a fun fact.
RUNNING AND TESTING THE API LOCALLY
To start the FastAPI server, I ran the following command:
**
uvicorn main:app --host 0.0.0.0 --port 8000**
I tested the API using a browser by navigating to:
The test was successful, indicated by the output below:
SETTING UP GITHUB
I initialized a Git repository, committed my files, and pushed them to GitHub:
git init
git add .
git commit -m "Initial commit"
git branch -M main
git remote add origin https://github.com/OmachokoYakubu/HNG-12-DevOps-Projects
git push -u origin main
This ensures my code is version-controlled and publicly available. There was an initial struggle with authentication because GitHub doesn’t accept SSH connections, but I went past that by generating an access token, which resolved the problem.
DEPLOYING THE FASTAPI API ON AWS
Having completed the above configurations, my next step was to deploy the FastAPI on AWS. This required creating an EC2 instance that I named **HNG12 API Server**. I Chose Ubuntu 22.04 LTS (free tier eligible); t3.micro was already set by default for my region, then I used the following network configurations:
Allow inbound traffic for SSH (port 22).
Allow HTTP (port 80) and HTTPS (port 443) for web access.
After launching, the instance was assigned the following IP address:
13.60.180.189
Since SSH access was configured as described above, I proceeded to access the server using the following command:
ssh -i ~/.ssh/HNG-12APIServerKP.pem ubuntu@13.60.180.189
This gave me instant access to the AWS EC2 instance, where I installed the following dependencies:
sudo apt update && sudo apt upgrade -y
sudo apt install python3 python3-pip git curl nginx -y
pip install fastapi uvicorn requests
The commands above effectively enabled Python, pip, git, curl, NGINX and FastAPI in the EC2 instance deployed on AWS.
TRANSFER OF API CODE FROM LOCAL VM TO AWS
In order to transfer the API code from my local VM to AWS, I cloned the GitHub repository previously configured using the following command:
git clone https://github.com/OmachokoYakubu/HNG-12-DevOps-Projects
I navigated in to the repository’s directory to test the functionality of the API on AWS, using the following command:
uvicorn main:app --host 0.0.0.0 --port 8000
It worked perfectly!
CONFIGURING NGINX AS A REVERSE PROXY
Configuring nginx as a reverse proxy required the following crucial steps:
Creating a Configuration File
I navigated to the following directory, and issued the accompanying command as follows:
*cd /etc/nginx/sites-available
sudo nano hng12.conf *
Afterwards, I inputted the following program:
server {
listen 80;
server_name 13.60.180.189; # Your public IP or domain name
location / {
proxy_pass http://127.0.0.1:8000; # Your API's address and port
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
In order for the NGINX server to run smoothly, I confirmed that the main NGINX configuration files included the configuration file above using the following command:
cat /etc/nginx/nginx.conf
The output of the command above is as follows:
user www-data;
worker_processes auto;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/.conf;
events {
worker_connections 768;
# multi_accept on;
}
http {
##
# Basic Settings
##
sendfile on;
tcp_nopush on;
types_hash_max_size 2048;
# server_tokens off;
# server_names_hash_bucket_size 64;
# server_name_in_redirect off;
include /etc/nginx/mime.types;
default_type application/octet-stream;
##
# SSL Settings
##
ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; # Dropping SSLv3, ref: POODLE
ssl_prefer_server_ciphers on;
##
# Logging Settings
##
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
##
# Gzip Settings
##
gzip on;
# gzip_vary on;
# gzip_proxied any;
# gzip_comp_level 6;
# gzip_buffers 16 8k;
# gzip_http_version 1.1;
# gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
##
# Virtual Host Configs
##
include /etc/nginx/conf.d/.conf;
include /etc/nginx/sites-enabled/hng12.conf; # line 60
}
mail {
# See sample authentication script at:
# http://wiki.nginx.org/ImapAuthenticateWithApachePhpScript
# auth_http localhost/auth.php;
# pop3_capabilities "TOP" "USER";
# imap_capabilities "IMAP4rev1" "UIDPLUS";
server {
listen localhost:110;
protocol pop3;
proxy on;
}
server {
listen localhost:143;
protocol imap;
proxy on;
}
}
Line 60 above (in italics) includes the configuration file which makes nginx a reverse proxy. The nginx test proved that the configurations above were correct.
TESTING THE LIVE API
To finally determine that the live API is fully functional, I tested it on a browser using the folllowing URL:
http://13.60.180.189/api/classify-number?number=371
It was a resounding success!!!
Further tests with negative integers and non-integers gave unique results:
CREATION OF README.md
The climax of this project was the creation of the README.md file. I navigated into the project directory on the AWS instance, and issued the following command:
nano README.md
I followed that up by pasting the following instructions into the nano text editor:
**# Number Classification API
This API takes a number as input and returns interesting mathematical properties about it, along with a fun fact.