{"id":23173888,"url":"https://github.com/dowusubekoe-dev/demo-movie-api","last_synced_at":"2026-04-11T07:01:13.426Z","repository":{"id":268636784,"uuid":"905006412","full_name":"dowusubekoe-dev/demo-movie-api","owner":"dowusubekoe-dev","description":null,"archived":false,"fork":false,"pushed_at":"2024-12-21T08:54:23.000Z","size":51,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-10-18T23:31:14.658Z","etag":null,"topics":["api","docker","dockerfile","git","json","linux-shell","postman-test","python"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dowusubekoe-dev.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-12-18T01:13:46.000Z","updated_at":"2024-12-21T08:54:27.000Z","dependencies_parsed_at":"2024-12-18T02:28:02.369Z","dependency_job_id":"f867f271-d74b-4da5-a5db-10f6256b2561","html_url":"https://github.com/dowusubekoe-dev/demo-movie-api","commit_stats":null,"previous_names":["dowusubekoe-dev/demo-movie-api"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/dowusubekoe-dev/demo-movie-api","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dowusubekoe-dev%2Fdemo-movie-api","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dowusubekoe-dev%2Fdemo-movie-api/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dowusubekoe-dev%2Fdemo-movie-api/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dowusubekoe-dev%2Fdemo-movie-api/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dowusubekoe-dev","download_url":"https://codeload.github.com/dowusubekoe-dev/demo-movie-api/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dowusubekoe-dev%2Fdemo-movie-api/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31671630,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-10T17:19:37.612Z","status":"online","status_checked_at":"2026-04-11T02:00:05.776Z","response_time":54,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["api","docker","dockerfile","git","json","linux-shell","postman-test","python"],"created_at":"2024-12-18T05:18:08.226Z","updated_at":"2026-04-11T07:01:13.399Z","avatar_url":"https://github.com/dowusubekoe-dev.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Simple REST API locally on a Linux server using Python with the Flask Framework (Dev)\n\n## Prerequisites:\n1. Linux server (local or remote).\n2. Python (version 3.x).\n3. pip (Python's package installer).\n\n### Steps to Build the API:\n\n1. Install Required Tools\nOpen the terminal on your Linux server and run:\n\n```bash\n# Install Python3 and pip if not already installed\nsudo apt update\nsudo apt install python3 python3-pip -y\n```\n\n2. Install Flask\nUse pip to install Flask:\n\n```bash\npip3 install flask\n```\n\n3. Create the API Project Directory\nNavigate to your preferred folder and create a new project directory:\n\n```bash\nmkdir my_api\ncd my_api\n```\n\n4. Write the API Code\nOpen the app.py file using a text editor (like nano, vim, or VSCode), and paste the following code:\n\n```python\nfrom flask import Flask, jsonify, request\n\napp = Flask(__name__)\n\n# Root Endpoint\n@app.route('/')\ndef home():\n    return jsonify({\"message\": \"Welcome to my API!\"})\n\n# Simple GET Endpoint\n@app.route('/api/greet', methods=['GET'])\ndef greet():\n    name = request.args.get('name', 'World')\n    return jsonify({\"greeting\": f\"Hello, {name}!\"})\n\n# Simple POST Endpoint\n@app.route('/api/echo', methods=['POST'])\ndef echo():\n    data = request.json\n    return jsonify({\"received_data\": data})\n\n# Run the Flask app\nif __name__ == '__main__':\n    app.run(host='127.0.0.1', port=5002, debug=True)\n```\n\n5. Run the API Server\nStart the Flask API server by running:\n\n```bash\npython3 app.py\n```\nBy default, Flask will start the server on port 5000. But for this app I used port 5002 because I had a docker image also running on port 5000\nEnsure port 5002 is open in your firewall settings.\n\n```bash\nsudo ufw allow 5002\n```\n\n### Test the API\n\n**a) Test the Root Endpoint** \nOpen your browser or use curl:\n\n```bash\ncurl http://127.0.0.1:5002/\n```\nExpected Output:\n\n```json\n{\"message\": \"Welcome to my API!\"}\n```\n\n**b) Test the GET Endpoint with Query Parameters**\n\n```bash\ncurl \"http://127.0.0.1:5002/api/greet?name=John\"\n```\n\nExpected Output:\n\n```json\n{\"greeting\": \"Hello, John!\"}\n```\n\n**c) Test the POST Locally** \n\nUse curl to send JSON data:\n\n```bash\ncurl -X POST -H \"Content-Type: application/json\" -d '{\"key\": \"value\"}' http://127.0.0.1:5002/api/echo\n```\n\nExpected Output:\n\n```json\n{\"received_data\": {\"key\": \"value\"}}\n```\n\nStop the Server\nTo stop the server, press Ctrl + C.\n\n### Test the API\n\nTo access the data from the movie database, **routes** will be used and to test if the app works and make it accessible to other devices on the same network, a route to display a simple message is used.\n\n```python\n@app.route(\"/\")\ndef home():\n    return jsonify({\"message\": \"Welcome to the Flask API!\"})\n```\nBy default, Flask binds to 127.0.0.1 (localhost), which means it only listens for requests from the same machine. To allow external access, ensure the host is set to 0.0.0.0 in your app.run():\n\n```python\nif __name__ == \"__main__\":\n    app.run(host=\"0.0.0.0\", port=5002, debug=True)\n```\nThis change makes Flask listen on all network interfaces, including the machine that the app is running on. E.g a linux server.\n\n\n#### Final code for testing the app.\n\n```python\nfrom flask import Flask, jsonify, request\n\napp = Flask(__name__)\n\n@app.route(\"/\")\ndef home():\n    return jsonify({\"message\": \"Welcome to the Flask API!\"})\n\nif __name__ == \"__main__\":\n    app.run(host=\"0.0.0.0\", port=5002, debug=True)\n```\n\n1. To set the api internally, run\n\n```bash\npython3 app.py\n```\nClick on the link by using Ctrl + Click or copy URL and paste in the browser. test root endpoint using this command\n\nExpected output:\n\n![](./images/test-api.PNG)\n\n\n2. For externnal testing of the api, using **curl**\n\n```bash\ncurl http://127.0.0.1:5002/\n```\nExpected output:\n\n```bash\n$ curl http://19x.xxx.xxx.xxx:5002\n  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current\n                                 Dload  Upload   Total   Spent    Left  Speed\n100    45  100    45    0     0   2502      0 --:--:-- --:--:-- --:--:--  2647\n{\n  \"message\": \"Welcome to the Flask API!\"\n}\n```\nThis section helped to get a better understanding of how set up APIs locally or externally and run the POST and GET requests or use **curl** command.\n\n---\n\n\n#  Prepare Flask API for Production using SQLite, Gunicorn, Nginx and Docker\n\nFor this section, I will be looking into how to store the movie data to a database and modify the **app.py** to add **routes** for *adding*, *viewing*, and *updating* movie data.\n\n\n## Run Flask API with Gunicorn (Production WSGI Server)\n\n1. Install **Gunicorn**\nFirst, install Gunicorn using pip3\n\n```bash\npip3 install gunicorn\n```\n\n2. Run Gunicorn with Flask\nNavigate to your project directory (where app.py is located) and run\n\n```bash\ngunicorn --bind 0.0.0.0:5002 app:app\n```\nExplanation:\n- app before : is the filename (app.py)\n- app after : is the Flask instance created in the file\n\n---\n\n\n## Set Up Nginx as a Reverse Proxy\n\n1. Install **Nginx**\nInstall Nginx on the Linux server\n\n```bash\nsudo apt update\nsudo apt install nginx -y\n```\n\n2. Configure Nginx\nCreate a new Nginx configuration file for your Flask app\n\n```bash\nsudo nano /etc/nginx/sites-available/flask_api\n```\nAdd the following configuration\n\n```nginx\nserver {\n    listen 80;\n    server_name your_server_ip_or_domain;\n\n    location / {\n        proxy_pass http://127.0.0.1:5002;\n        proxy_set_header Host $host;\n        proxy_set_header X-Real-IP $remote_addr;\n        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n        proxy_set_header X-Forwarded-Proto $scheme;\n    }\n}\n```\nReplace:\n- ****your_server_ip_or_domain** with the linux server ip address\n\n3. Enable the Configuration\nRun the following commands\n\n```bash\nsudo ln -s /etc/nginx/sites-available/flask_api /etc/nginx/sites-enabled/\nsudo nginx -t  # Test Nginx configuration\nsudo systemctl restart nginx\n```\n---\n\n\n## Run Flask App with Gunicorn as a Service\n\nTo keep your Gunicorn app running, create a systemd service:\n\n1. Create the service file\n\n```bash\nsudo nano /etc/systemd/system/flask_api.service\n```\n\n2. Add the following configuration\n\n```ini\n[Unit]\nDescription=Gunicorn instance to serve Flask API\nAfter=network.target\n\n[Service]\nUser=\u003cyour_linux_username\u003e\nGroup=www-data\nWorkingDirectory=/path/to/your/project\nEnvironment=\"PATH=/usr/bin\"\nExecStart=/usr/local/bin/gunicorn --workers 3 --bind unix:flask_api.sock -m 007 app:app\n\n[Install]\nWantedBy=multi-user.target\n```\nReplace:\n- **your_linux_username** with your Linux user.\n- **/path/to/your/project** with the absolute path to your Flask project.\n\n3. Start and enable the service\n\n```bash\nsudo systemctl daemon-reload\nsudo systemctl start flask_api\nsudo systemctl enable flask_api\n```\n\n4. Check the service status\n\n```bash\nsudo systemctl status flask_api\n```\n---\n\n\n## Dockerize the Flask API\n\n1. Create a Dockerfile\nIn **your project directory**, create a **Dockerfile**\n\n```Dockerfile\n# Use official Python image\nFROM python:3.10\n# Set working directory\nWORKDIR /app\n# Copy project files\nCOPY . /app\n# Install dependencies\nRUN pip install --no-cache-dir flask gunicorn\n# Expose the port Flask uses\nEXPOSE 5000\n# Run the API with Gunicorn\nCMD [\"gunicorn\", \"--bind\", \"0.0.0.0:5002\", \"app:app\"]\n```\n\n2. Build the Docker Image\nRun the following command in **your project directory**\n\n```bash\ndocker build -t flask_api .\n```\n\n3. Run the Docker Container\nRun the container to expose port 5002 or your the port of the app\n\n```bash\ndocker run -d -p 5002:5002 flask_api\n```\n\nTest the API with;\n\n```bash\ncurl http://127.0.0.1:5002/ # internally\n```\nOR\n\n```bash\ncurl http://1xx.1xx.xx.xxx:5002/ # externally\n```\n\n---\n\n\n## Connect Dockerized API to Nginx\n\nFor simplicity, I decided to run both **Nginx** and **Docker** on the same machine.\n\n1. Modify the Nginx configuration **(/etc/nginx/sites-available/flask_api)**\n\n```nginx\nlocation / {\n    proxy_pass http://127.0.0.1:5002;\n}\n```\n\n2. Restart Nginx\n\n```bash\nsudo systemctl restart nginx\nsudo systemctl status nginx\n```\nIn this section, I configured Flask to run with Gunicorn, set up Nginx as a reverse proxy and containerized the API with Docker for portability.\n\n---\n\n\n## Reload Systemd, Restart Service\n\nOnce the change is made:\n\n```bash\nsudo systemctl daemon-reload\nsudo systemctl restart flask_api\n```\n\nCheck the status of the service:\n\n```bash\nsudo systemctl status flask_api\n```\n---\n\n\n## Test Endpoint in Postman\n\nAssuming your Nginx is reverse-proxying requests to the Gunicorn API, test it via your server's IP address or domain name.\n\n**a) Test the Root Endpoint**\nRun the following command\n\n```bash\ncurl http://\u003cyour_server_ip_or_domain\u003e/ # You should see the response: {\"message\": \"Welcome to my API!\"}\n```\n\n**b) Test the GET Endpoint with Query Parameters**\n\n```bash\ncurl \"http://\u003cyour_server_ip_or_domain\u003e:\u003cport\u003e/api/greet?name=John\" # {\"greeting\": \"Hello, John!\"}\n```\n\n**c) Test the POST Endpoint**\nSend JSON data to the API\n\n```bash\ncurl -X POST -H \"Content-Type: application/json\" -d '{\"key\": \"value\"}' http://\u003cyour_server_ip_or_domain\u003e/api/echo # {\"received_data\": {\"key\": \"value\"}}\n```\n\n### Test with Postman\n\n1. Download and open Postman (or use its web version).\n2. Create a new request:\n    - Method: GET or POST\n    - URL: http://\u003cyour_server_ip_or_domain\u003e/api/greet or any other endpoint.\n3. For the POST endpoint, go to Body \u003e raw and set the type to JSON, then provide input:\n\n```json\n{\"key\": \"value\"}\n```\n4. Send the request and view the response\n\n### Test with a Browser\n\n1. For GET endpoints like / or /api/greet, open the browser and enter\n\n```bash\nhttp://\u003cyour_server_ip_or_domain\u003e/\n```\n\n2. For /api/greet with query parameters\n\n```perl\nhttp://\u003cyour_server_ip_or_domain\u003e/api/greet?name=John\n```\n\n---\n\n\n## Plan Structure of Movie Data API\n\nStructure of the API will need the following information\n\n- Movie title\n- Genre\n- Release year\n- Cast and crew\n- Plot synopsis\n- Ratings\n- Poster image URL\n\n### Import Flask, Python and SQLite Dependencies\n\nSince I will be using using Python in buidling this app, I have to import Flask, jsonify, sqlit, and request from Flask.\n\n```python\nfrom flask import Flask, jsonify, request\nimport sqlite3\n\napp = Flask(__name__)\n```\n\n### Set Up Database\n\nUse a database (like SQLite, PostgreSQL, or MySQL) to store your movie data. For simplicity, you can start with SQLite since it requires minimal setup.\n\n1. Install SQLite\n\n```bash\nsudo apt-get install sqlite3\n```\n2. Create a Database Schema called (e.g movie_data.db)\n\n```bash\nsqlite3 movie_data.db\n```\n3. Create a basic table for the movie_data.db database\n\n```sql\nCREATE TABLE movies (\n    id INTEGER PRIMARY KEY AUTOINCREMENT,\n    title TEXT NOT NULL,\n    genre TEXT NOT NULL,\n    year INTEGER,\n    plot TEXT,\n    rating REAL,\n    poster_url TEXT\n);\n```\n\n### Connection to the SQLite Database\n\nCreate a function to connet to sqlite database.\n\n```python\n# Connect to SQLite DB\ndef get_db_connection():\n    conn = sqlite3.connect('movie_data.db')\n    conn.row_factory = sqlite3.Row\n    return conn\n```\n\n### Route to Fetch all Movie Data\n\nCreate another route and function in app.py to pull movie data from the database.\n\n```python\n@app.route('/api/movies', methods=['GET'])\ndef get_movies():\n    conn = get_db_connection()\n    movies = conn.execute('SELECT * FROM movies').fetchall()\n    conn.close()\n    return jsonify([dict(movie) for movie in movies])\n```\n\n### Request Movie Data by ID\n\n```python\n@app.route('/api/movies/\u003cint:id\u003e', methods=['GET'])\ndef get_movie(id):\n    conn = get_db_connection()\n    movie = conn.execute('SELECT * FROM movies WHERE id = ?', (id,)).fetchone()\n    conn.close()\n    if movie is None:\n        return jsonify({\"error\": \"Movie not found\"}), 404\n    return jsonify(dict(movie))\n```\n\n### Add New Movie Data to SQLite Database\n\n```python\n@app.route('/api/movies', methods=['POST'])\ndef add_movie():\n    data = request.get_json()\n    title = data['title']\n    genre = data['genre']\n    year = data.get('year', None)\n    plot = data.get('plot', None)\n    rating = data.get('rating', None)\n    poster_url = data.get('poster_url', None)\n\n    conn = get_db_connection()\n    conn.execute('INSERT INTO movies (title, genre, year, plot, rating, poster_url) VALUES (?, ?, ?, ?, ?, ?)',\n                 (title, genre, year, plot, rating, poster_url))\n    conn.commit()\n    conn.close()\n    return jsonify({\"message\": \"Movie added successfully!\"}), 201\n```\n\n### Update Existing Movie Details\n\n```python\n@app.route('/api/movies/\u003cint:id\u003e', methods=['PUT'])\ndef update_movie(id):\n    data = request.get_json()\n    title = data['title']\n    genre = data['genre']\n    year = data.get('year', None)\n    plot = data.get('plot', None)\n    rating = data.get('rating', None)\n    poster_url = data.get('poster_url', None)\n\n    conn = get_db_connection()\n    conn.execute('UPDATE movies SET title = ?, genre = ?, year = ?, plot = ?, rating = ?, poster_url = ? WHERE id = ?',\n                 (title, genre, year, plot, rating, poster_url, id))\n    conn.commit()\n    conn.close()\n    return jsonify({\"message\": \"Movie updated successfully!\"})\n```\n\n### Delete Movie Data by ID\n\n```python\n@app.route('/api/movies/\u003cint:id\u003e', methods=['DELETE'])\ndef delete_movie(id):\n    conn = get_db_connection()\n    conn.execute('DELETE FROM movies WHERE id = ?', (id,))\n    conn.commit()\n    conn.close()\n    return jsonify({\"message\": \"Movie deleted successfully!\"})\n```\n\n....\n\n\n---\n\n\n## Test the Movie API\n\n1. **GET all movies:**\n\n```bash\ncurl http://127.0.0.1:5002/api/movies\n```\n\n2. **GET a Movie by ID**\n\n```bash\ncurl http://127.0.0.1:5002/api/movies/1\n```\n\n3. **POST a new moview**\n\n```bash\ncurl -X POST -H \"Content-Type: application/json\" -d '{\n  \"title\": \"Inception\",\n  \"genre\": \"Sci-Fi\",\n  \"year\": 2010,\n  \"plot\": \"A mind-bending thriller.\",\n  \"rating\": 8.8,\n  \"poster_url\": \"https://link_to_poster.com\"\n}' http://127.0.0.1:5002/api/movies\n```\n\n4. **PUT to update a movie**\n\n```bash\ncurl -X PUT -H \"Content-Type: application/json\" -d '{\n  \"title\": \"Inception\",\n  \"genre\": \"Sci-Fi\",\n  \"year\": 2010,\n  \"plot\": \"A new plot description.\",\n  \"rating\": 9.0,\n  \"poster_url\": \"https://new_link_to_poster.com\"\n}' http://127.0.0.1:5000/api/movies/1\n```\n5. **DELETE a movie**\n\n```bash\ncurl -X DELETE http://127.0.0.1:5002/api/movies/1\n```\n\n---\n\n\n## Load Movie Data from a JSON file into Database\n\nTo load more movie data from a JSON file and add it to your database, you'll need to:\n\n1. Prepare the JSON File: Make sure your JSON file contains movie data in a format that matches your database schema.\n2. Write a script to load data into the database.\n\n\n### Prepare the JSON File\n\n- Create a JSON file called movies.json in format below\n\n```json\n[\n    {\n        \"title\": \"The Dark Knight\",\n        \"genre\": \"Action\",\n        \"year\": 2008,\n        \"plot\": \"Batman faces the Joker, a criminal mastermind.\",\n        \"rating\": 9.0,\n        \"poster_url\": \"https://example.com/dark-knight.jpg\"\n    },\n    {\n        \"title\": \"Interstellar\",\n        \"genre\": \"Sci-Fi\",\n        \"year\": 2014,\n        \"plot\": \"A team of astronauts explores a wormhole in search of a new home for humanity.\",\n        \"rating\": 8.6,\n        \"poster_url\": \"https://example.com/interstellar.jpg\"\n    }\n]\n```\n\n\n### Write a Script to Load Movies into the Database\n\nCreate a Python script (load_movies.py) to load the movie data from movies.json into your SQLite database.\n\n```python\nimport sqlite3\nimport json\n\n# Connect to SQLite DB\ndef get_db_connection():\n    conn = sqlite3.connect('movie_data.db')\n    conn.row_factory = sqlite3.Row\n    return conn\n\n# Load movies from JSON file\ndef load_movies_from_json(file_path):\n    with open(file_path, 'r') as file:\n        movies = json.load(file)\n    return movies\n\n# Insert movies into the database\ndef insert_movies(movies):\n    conn = get_db_connection()\n    for movie in movies:\n        conn.execute('''\n            INSERT INTO movies (title, genre, year, plot, rating, poster_url)\n            VALUES (?, ?, ?, ?, ?, ?)\n        ''', (movie['title'], movie['genre'], movie['year'], movie['plot'], movie['rating'], movie['poster_url']))\n    conn.commit()\n    conn.close()\n\n# Main function to load the movies\ndef main():\n    movies = load_movies_from_json('movies.json')\n    insert_movies(movies)\n    print(f'{len(movies)} movies loaded successfully!')\n\nif __name__ == '__main__':\n    main()\n```\n\n### Run script and verify data\n\nAfter saving the script as load_movies.py, run it to load the movies from the movies.json file into your database:\n\n```bash\npython3 load_movies.py\n```\n\nYou can check the database to ensure the movies have been added correctly by running:\n\n```bash\nsqlite3 movie_data.db\n```\n\nThen, run the query to view the movies:\n\n```sql\nSELECT * FROM movies;\n```\n\n\n\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdowusubekoe-dev%2Fdemo-movie-api","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdowusubekoe-dev%2Fdemo-movie-api","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdowusubekoe-dev%2Fdemo-movie-api/lists"}