pythonrestbackendfastapiendpoint

FastAPI conflict path parameter in endpoint - good practices?


I am creating 2 GET methods for a resource student using FastAPI. I'm looking to GET a student in 2 ways: by student_id or by student_name.

The issue is that, I initially created the 2 endpoints as follows

@app.get("/student/{student_name}", response_model=schemas.Student, status_code=200)
def get_student_by_name(student_name: str, db: Session = Depends(get_db)):
    db_student = crud.get_student_by_name(db, student_name)
    if db_student is None:
        raise HTTPException(status_code=404, detail="Student not found")
    return db_student


@app.get("/student/{student_id}", response_model=schemas.Student, status_code=200)
def get_student_by_id(student_id: int, db: Session = Depends(get_db)):
    db_student = crud.get_student_by_id(db, student_id)
    if db_student is None:
        raise HTTPException(status_code=404, detail="Student not found")
    return db_student

The problem is that the endpoint names are conflicting with each other, it is both /student followed by a parameter and only one of them could work - in this case only /student/{student_name} because it is defined in the front. So I came up with this simple workaround by adding a bit more to the endpoint names:

@app.get("/student/{student_name}", response_model=schemas.Student, status_code=200)
def get_student_by_name(student_name: str, db: Session = Depends(get_db)):
    db_student = crud.get_student_by_name(db, student_name)
    if db_student is None:
        raise HTTPException(status_code=404, detail="Student not found")
    return db_student


@app.get("/student/byid/{student_id}", response_model=schemas.Student, status_code=200)
def get_student_by_id(student_id: int, db: Session = Depends(get_db)):
    db_student = crud.get_student_by_id(db, student_id)
    if db_student is None:
        raise HTTPException(status_code=404, detail="Student not found")
    return db_student

I added /byid to the endpoint name of the get_student)by_id method. While both endpoints could work now, I am wondering if this is considered a good practice? WHat would be the best practice when one resource needed to be queried with a single path parameter to differentiate the endpoint names?


Solution

  • I will do something like this

    @app.get("/student/{student_id}", response_model=schemas.Student, status_code=200)
    def get_student(student_id: str, db: Session = Depends(get_db)):
        db_student = crud.get_student_by_id(db, student_id)
        if db_student is None:
            raise HTTPException(status_code=404, detail="Student not found")
        return db_student
    
    
    # use search criterias as query params
    @app.get("/student/", response_model=List[schemas.Student], status_code=200)
    def get_students(student_name: string = None, db: Session = Depends(get_db)):
        # Query inside your crud file
        query = db.query(Student)
        if student_name:
            # if you want to search similar items
            query = query.filter(Student.name.like(f"%{student_name}%"))
            # if you want to search an exact match
            query = query.filter(Student.name == student_name)
        
        return query.all()
    

    With this your code will be a little bit more open to future changes, I will only use a url param when searching by id, any other search criteria can be handled as a filter parameter using query params