Search code examples
pythonrestdjango-rest-frameworkapi-design

How do I use an API endpoint to call another API endpoint using Django REST Framework?


For context, I'm creating a feature on my app that allows users to delete their account, but first they need to validate their info like username, password, and type the string 'delete'. So for this validation I created a POST request that queries all this info from the database and if it validates to true I want to return that users ID and call my other DELETE request, passing the ID of the user in the URL.

So my idea is: after the first request validates to true, I want to immediately call the second request.

Here is my current POST endpoint:

@api_view(['POST'])
def validating_user_information_for_deletion(request, id):
    try:
        user = User.objects.get(id=id)

        current_username_from_request = request.data["username"]
        current_password_from_request = request.data["password"]
        delete_string_from_request = request.data["delete_string"]

        if current_username_from_request != user.username:
            return Response({"message": "username is wrong"})
        if not compare_hashed_passwords(current_password_from_request, user.password):
            return Response({"message": "invalid password"})
        
        if delete_string_from_request != "delete":
            return Response({"message": "invalid confirmation string"})
        
        # this is a post request
        # we should call the delete user request passing the id????
        return Response({"message": "delete method called", "ID": "id"})

    except Exception as error:
        return Response({"message": "something went wrong", "error": str(error)})

The respective URL:

    path('delete/account/<int:id>', views.delete_user_account, name='delete_user_account')

And here is my DELETE endpoint

@api_view(['DELETE'])
def delete_user(request, id):

    try:
        user_for_deletion = User.objects.get(id=id)
        user_for_deletion.delete()

        return Response({"message": "user deleted successfully"})
    
    except Exception as error:
        return Response({"message": str(error)})

and its respective URL:

    path('delete/user/<int:id>', views.delete_user, name="delete_user"),

Like I mentioned, my idea is to call the first endpoint in the frontend, then if it validates to true I want to call the DELETE request passing the ID.

The ID is automatically stored once the user is logged, that way I can access the user's id throughout the whole app.

Is it a good practice to have one API endpoint 'calling' another, or should I perform this using only a DELETE request?

btw, I'm using react on the frontend.


Solution

  • You mentioned calling 2 endpoints in the frontend, this is not good since it doubles the request count and can be opened to request manipulation attacks where an attacker would skip verification.

    Also there is no point in verifying the delete request data without deleting, and naming the request POST is misleading since there are side effects (a user is being deleted).

    I would create a single endpoint DELETE /users/:id that receives the validation data in the body:

        {
            "validation_info": {
                "username": "user",
                "password": "pass",
                "delete_string": "..."
            }
        }
    

    Then when implementing the function:

    @api_view(['DELETE'])
    def delete_user(request, id):
        try:
            user = User.objects.get(id=id)
    
            validation_info = request.data.get("validation_info", {})
            username = validation_info.get("username")
            password = validation_info.get("password")
            delete_string = validation_info.get("delete_string")
    
            if username != user.username:
                return Response({"message": "username is wrong"})
            if not check_password(password, user.password):
                return Response({"message": "invalid password"})
            if delete_string != "delete":
                return Response({"message": "invalid confirmation string"})
    
            user.delete()
            return Response({"message": "user deleted successfully"})
    
        except Exception as error:
            return Response({"message": "something went wrong", "error": str(error)})