pythonnginxgunicornfastapiinternal-server-error

Why up and running web service returns 500 Internal Server Error?


I'm learning back-end and decided to write a simple web service using FastAPI. The idea is, the front-end does the request to the webserver, then the webserver communicates with Postgresql DB to retrieve the data, and then the webserver returns that data to the front-end. I'm using FastAPI for creating a web service, Nginx to expose that service to the internet, Postgresql for storing the data.
Here is my code for that service:

from fastapi import FastAPI, HTTPException
import psycopg2
import time

app = FastAPI()

while True:
    try:
        # Connect to your postgres DB
        conn = psycopg2.connect(host='hostIP', database='postgres',user='postgres',password='passwd')

        # Open a cursor to perform database operations
        cursor = conn.cursor()
        print('DB connection was successfull!')
        break
    except Exception as error:
        print('Connection to DB failed')
        print("Error: ", error)
        time.sleep(2)

@app.get("/")
async def root():
    cursor.execute("""SELECT * FROM characters""")
    flags = cursor.fetchall()
    return {'data': flags}

hostIP = is IP address of my ubuntu server (for security purpose I'm not showing actual IP here) which is installed on VirtualBox VM. So my web service and database both are deployed on this VM.
Then I created a gunicorn.service to run fastAPI service:

[Unit] Description=Gunicorn Web Server as Unit Service Systemd After=network.target

[Service]
User=ubuntuserver
Group=ubuntuserver
WorkingDirectory=/home/ubuntuserver/test
Environment="PATH=/home/ubuntuserver/test/venv/bin"
ExecStart=/home/ubuntuserver/test/venv/bin/gunicorn --config /home/ubuntuserver/test/main.py main:app

[Install]
WantedBy=multi-user.target

For nginx, I configured default.conf file like below:

server {
  listen 80;
  server_name hostIP;
  location / {
    proxy_pass http://localhost:8000;
  }
}

So I expect to type in on another computer's browser, hostIP:80 and get database information or at least some success message etc. but instead I get Internal Server Error.
On server-side, I ran below command to check the services:

systemctl status postgresql 
systemctl status gunicorn.service  
systemctl status nginx  

And they all appear to be normally activated.

Below I checked all ports in use by services:
ports

Am I missing something ?

EDIT
After using suggestion by Thomas and running below command:

sudo journalctl -f -u nginx -u gunicorn  

I got folowing output:

Feb 06 12:08:31 ubuntuserver gunicorn[3975]: [2022-02-06 12:08:31 +0000] [3975] [ERROR] Error handling request /
Feb 06 12:08:31 ubuntuserver gunicorn[3975]: Traceback (most recent call last):
Feb 06 12:08:31 ubuntuserver gunicorn[3975]: File "/home/ubuntuserver/test/venv/lib/python3.8/site-packages/gunicorn/workers/sync.py", line 136, in handle
Feb 06 12:08:31 ubuntuserver gunicorn[3975]: self.handle_request(listener, req, client, addr)
Feb 06 12:08:31 ubuntuserver gunicorn[3975]:File "/home/ubuntuserver/test/venv/lib/python3.8/site-packages/gunicorn/workers/sync.py", line 179, in handle_request
Feb 06 12:08:31 ubuntuserver gunicorn[3975]: respiter = self.wsgi(environ, resp.start_response)
Feb 06 12:08:31 ubuntuserver gunicorn[3975]: TypeError: _call_() missing 1 required positional argument: 'send'


Solution

  • Finally, resolved the issue, so FastAPI uses ASGI which is asynchronous, and gunicorn, where I run my FastAPI application, uses WSGI which is synchronous. When executing gunicorn command you should add uvicorn's workers. In my case, I've edited my gunicorn.service file:

    [Unit] Description=Gunicorn Web Server as Unit Service Systemd After=network.target
    
    [Service]
    User=ubuntuserver
    Group=ubuntuserver
    WorkingDirectory=/home/ubuntuserver/test
    Environment="PATH=/home/ubuntuserver/test/venv/bin"
    ExecStart=/home/ubuntuserver/test/venv/bin/gunicorn --config /home/ubuntuserver/test/main.py main:app -w 4 -k uvicorn.workers.UvicornWorker
    
    [Install]
    WantedBy=multi-user.target
    

    Note that to use uvicorn workers, you need to install the below components first:

    uvloop
    httptools