title: Making Safe API Calls With Python published: true description: A tutorial on using returns library to make safe API calls tags: python, returns, APIs


Making REST API calls in python is quite common and easy especially due to existince of libraries such as requests.

Take the following example: python def get_user(user_id): res = requests.get(f'https://jsonplaceholder.typicode.com/users/{user_id}') if res.ok: return res.json() else: print(f'{res.status_code}: {res.text}') return {}

The code is pretty straightforward. We are defining a function to fetch a user's details from an API (A very handy web app for testing out web apps).

Simple. Right?

However, what happens when there is no connectivity? Exceptions are raised. This can easily be wrapped in a try...except clause but it makes our code messy, especially when we have multiple endpoints to call.

Enter returns.

Grand entrance

Returns is a library written with safety in mind. It's quite powerful and has a lot to offer. Now let's quickly fix our unsafe code. ```python class GetUser(object):

@pipeline
def __call__(self, user_id: int) -> Result[dict, Exception]:
    res = self._make_request(user_id).unwrap()
    return self._parse_json(res)

@safe
def _make_request(self, user_id: int) -> requests.Response:
    res = requests.get(f'https://jsonplaceholder.typicode.com/users/{user_id}')
    res.raise_for_status()
    return res

@safe
def _parse_json(self, res: requests.Response) -> dict:
    user = res.json()
    return user

get_user = GetUser()

```

We have defined a class which can be invoked as a function once instantiated. We need to take note of the @safe decorator, a very handy decorator which wraps any exception raised into a Failure object. The Failureclass is a subclass of the Result type which is basically a simple container. Now, our code changes from: python try: user = get_user(1) except Exception: print('An error occurred') to: python user = get_user(1) # returns Success(dict) or Failure(Exception) user = user.value_or({}) Isn't that just elegant?

I know what you are thinking. It's too much code! That's the same thing I thought at first. But after playing around with the library and a bit of customization, I started to notice that my code looked cleaner. I could quickly scan through a file and get the gist of what is going without have to follow references. This is just the surface. Returns has way more to offer. Check it out.

Here's a decorator I came up with to automatically unwrap Result inner values or provide a default value instead: ```python def unwrap(success=None, failure=None): def outer_wrapper(func): @functools.wraps(func) def wrapper(args, kwargs): result = func(args, **kwargs) if is_successful(result): if success is not None: return success else: if failure is not None: return failure return result._inner_value

    return wrapper

return outer_wrapper

Usage:python @unwrap(failure={}) # If a failure occurs an empty dict is returnd @pipeline def call(self, user_id: int) -> Result[dict, Exception]: ...

user = get_user(1) # returns dict or empty dict
```

Cheers!