Communicate via JSON payload and upload files in FastAPI!

Tired of wandering around documentation and blogs? Here is a roadmap to help you find this lost treasure!

Ginelle D'souza
Geek Culture
7 min readSep 24, 2021

--

Source: Official FastAPI Website

Introduction

How simple would life be if we could put across our thoughts with utmost clarity? Being able to communicate with others effectively makes a powerful impact on the results we need to achieve. Therefore we say, “Communication is the key to success”. This phrase holds in the world of technology as well. Applications communicate through an “Application Programming Interface (API)” as humans communicate through languages. An API allows applications to share information (i.e., Lay man’s thoughts) with utmost consistency! Communication is the key to a good build system architecture.

An API is a type of interface that offers services to a computer or a computer program. When a system requires services, it requests the API for a response. The API is responsible to accept the request and process it or reject the request and acknowledge it. If the API accepts the request, it must perform a predefined task and respond. In this article, we will explore the functionalities of FastAPI.

FastAPI was released in 2018 and developed by Sebastián Ramírez. This API is used to create web applications in python and works with Uvicorn and Gunicor web servers. FastAPI supports asynchronous programming, pydantic, and the most impressive of all OpenAPI Specification. These are a few of the many features that FastAPI holds; we will be using the above-listed features in this article. So let us now quickly understand these three features.

Asynchronous Programming

FastAPI allows a program to perform other activities while it waits for the resources from another program/process. A module defined with the keyword async makes the module asynchronous. A request in an asynchronous module has the keyword await. The await keyword sends the request to a pool/bucket rather than blocking the module.

Pydantic

Python has gifted us a language where we need not define the data type of our variables. It may seem like a blessing, but while handling multiple APIs, undefined data will cost our program several problems. Therefore, through the powerful pydantic library, we can enable data validation within our python scripts. It is possible by creating a base model that encloses all the variables, their types, and default values (optional)

OpenAPI Specification

The OpenAPI Specification (OAS), formerly known as Swagger Specification, defines an interface that allows the developer to understand the service without creating an entire product (program). A user can understand and interact with the remote service. All we have to do is access the OAS through the “/docs” directory. In other words, simply access “http://127.0.0.1:8000/docs

Now that we have a clear picture of what we will explore, let us dive deep into the topic. In this article, we will demonstrate three techniques in FastAPI.

  • Communication through “JSON Payload”
  • Uploading a file
  • A combination of accepting data and file uploads

My GitHub safely stores the above-hidden gems and provides a summarized process to triumph.

So, let’s get coding!

Laying down the roadmap

Code

#FASTAPI imports
from fastapi import FastAPI, Request, File, UploadFile, Depends
from pydantic import BaseModel
#APP defination
app = FastAPI()
#Base model
class Options (BaseModel):
FileName: str
FileDesc: str = “Upload for demonstration”
FileType: Optional[str]

Base Model:

By using the pydantic technique, we define the base model. Here the data type of the variables is specified. Here we see three types of definition:

1. FileName: str — The variable named “FileName” accepts a string value.

2. FileDesc: str = “Upload for demonstration” — The variable named “FileDesc” accepts string value and has a default value assigned.

3. FileType: Optional[str] — The variable named “FileType” accepts a string value that is not mandatory. A user may choose not to provide input for such variables.

Destination 1: Communication through “JSON Payload”

A payload is the actual data pack sent in an HTTP request. It is a piece of critical information when we make an API request. This payload can be in several formats. We will look into the JSON format in particular. A majority of APIs communicate via a JSON payload.

Code

#Using an asynchronous POST method for communication@app.post("/acceptdata")
async def get_data(request: Request,options: Options):

#Waits for the request and converts into JSON
result = await request.json()

#Prints result in cmd – verification purpose
print(result)
return result

Code Explanation

  1. @app.post(“/acceptdata”) — In the previous code, we had initialized our application. We now inform the API that the data path “/acceptdata” must be sent via a POST request.
  2. async def get_data(request: Request,options: Options) — At the beginning of this article, we came across “Asynchronous Programming” offered by FastAPI. Therefore, within this function, we use the feature of asynchronous programming. (Read More…)
  3. result = await request.json() — Here two operations are working simultaneously.

3.1. await — If the required resources are blocked, await sends the request to a pool/bucket. (Read More…)

3.2. request.json() — The request received is converted into a JSON payload.

(Note: Across all the output images, the matter of interest is in red highlights)

Output

We will now test our script by selecting “Try it out”.

Testing : “/acceptdata”

Fill in the variables with appropriate values.

Enter values within the request body

The response return will be displayed as follows.

Once successfully executed the OAS displays the results within the response body

By default, when we return the data as a successful response, it is displayed in a JSON format. Since we have printed the request, let us view the command prompt to verify whether we have successfully received the request as a JSON Payload.

JSON payload printed in python script, displays result in command prompt

Voilà, we have achieved a request in a JSON form.

Destination 2: Uploading a file

Uploading a file can be done with the UploadFile and File class from the FastAPI library. Let us keep this simple by just creating a method that allows the user to upload a file. Once uploaded, we will display the name of the file as a response as well as print it for verification in the command prompt.

Code

#Upload a file and return filename as response@app.post("/uploadfile")
async def create_upload_file(data: UploadFile = File(...)):
#Prints result in cmd – verification purpose
print(data.filename)
#Sends server the name of the file as a response
return {"Filename": data.filename}

Code Explanation

  1. async def create_upload_file(data: UploadFile = File(…)) — There are two methods, “Bytes” and “UploadFile” to accept request files. Bytes work well when the uploaded file is small. For large files like images, videos, large binaries, etc., we use UploadFile. (Read More…)
  2. data.filename Here we return the name of the uploaded file.

Output

Let us test the script by selecting “Try it out” → “Choose File” → Locate the file.

Uploading an image

The response return will be displayed as follows.

Once successfully executed the OAS displays the results within the response body

The Final Destination: A combination of accepting data and file uploads

We have successfully demonstrated passing data via a JSON payload and uploading files. So, a question comes to my mind is it possible to perform both operations within a single method? Yes, it is! We can create a function that supports this functionality with the help of the “Depends” class. A function may have dependencies for several reasons, such as code reusability, security, database connections, etc. A Dependant function is a function that uses another function/class to carry out its activities.

Code

#Accept request as data and file
@app.post("/uploadandacceptfile")
async def upload_accept_file(options: Options = Depends(),data: UploadFile = File(...)):
data_options = options.dict()

result = "Uploaded Filename: {}. JSON Payload {}".format(data.filename,data_options)

Code Explanation

  1. async def upload_accept_file(options: Options = Depends(),data: UploadFile = File(…)): — The most important feature within this function definition is the “depend()”. The dependency method informs the “upload_accept_file” function that the parametrized variable “options” depending on the class Options. (Read More…)
  2. data_options = options.dict() — Here we try another technique to convert the received request into an equivalent JSON Payload.

Output

Let us test the script by selecting “Try it out”.

Combination of base model with file uploads via dependency

The response return will be displayed as follows.

Once successfully executed the OAS displays the results within the response body

Hooray! We have finally completed this exciting adventure.

Thank you for reading! :)

--

--

Ginelle D'souza
Geek Culture

A data enthusiast eager to explore and share the true meaning of data.