FastAPI – Query Parameters

FastAPI – Query Parameters ”; Previous Next A classical method of passing the request data to the server is to append a query string to the URL. Assuming that a Python script (hello.py) on a server is executed as CGI, a list of key-value pairs concatenated by the ampersand (&) forms the query string, which is appended to the URL by putting a question mark (?) as a separator. For example − http://localhost/cgi-bin/hello.py?name=Ravi&age=20 The trailing part of the URL, after (?), is the query string, which is then parsed by the server-side script for further processing. As mentioned, the query string is a list of parameter=value pairs concatenated by & symbol. FastAPI automatically treats the part of the endpoint which is not a path parameter as a query string and parses it into parameters and its values. These parameters are passed to the function below the operation decorator. Example from fastapi import FastAPI app = FastAPI() @app.get(“/hello”) async def hello(name:str,age:int): return {“name”: name, “age”:age} Start the Uvicorn server and this URL in the browser − http://localhost:8000/hello?name=Ravi&age=20 You should get the same JSON response. However, checking the tells you that FastAPI has detected that /hello endpoint has no path parameters, but query parameters. Click the Try it out button, enter “Ravi” and “20” as values, and press the Execute button. The documentation page now shows Curl command, request URL, and the body and headers of HTTP response. Example You can use Python’s type hints for the parameters of the function to be decorated. In this case, define name as str and age as int. from fastapi import FastAPI app = FastAPI() @app.get(“/hello/{name}”) async def hello(name:str,age:int): return {“name”: name, “age”:age} Try entering http://localhost:8000/docs as the URL. This will open the Swagger UI (OpenAPI) documentation. The parameter ”name” is a path parameter and ”age” is a query parameter Print Page Previous Next Advertisements ”;

FastAPI – Hello World

FastAPI – Hello World ”; Previous Next Getting Started The first step in creating a FastAPI app is to declare the application object of FastAPI class. from fastapi import FastAPI app = FastAPI() This app object is the main point of interaction of the application with the client browser. The uvicorn server uses this object to listen to client’s request. The next step is to create path operation. Path is a URL which when visited by the client invokes visits a mapped URL to one of the HTTP methods, an associated function is to be executed. We need to bind a view function to a URL and the corresponding HTTP method. For example, the index() function corresponds to ‘/’ path with ‘get’ operation. @app.get(“/”) async def root(): return {“message”: “Hello World”} The function returns a JSON response, however, it can return dict, list, str, int, etc. It can also return Pydantic models. Save the following code as main.py from fastapi import FastAPI app = FastAPI() @app.get(“/”) async def index(): return {“message”: “Hello World”} Start the uvicorn server by mentioning the file in which the FastAPI application object is instantiated. uvicorn main:app –reload INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit) INFO: Started reloader process [28720] INFO: Started server process [28722] INFO: Waiting for application startup. INFO: Application startup complete. Open the browser and visit http://localhost:/8000. You will see the JSON response in the browser window. Print Page Previous Next Advertisements ”;

FastAPI – Parameter Validation

FastAPI – Parameter Validation ”; Previous Next It is possible to apply validation conditions on path parameters as well as query parameters of the URL. In order to apply the validation conditions on a path parameter, you need to import the Path class. In addition to the default value of the parameter, you can specify the maximum and minimum length in the case of a string parameter. from fastapi import FastAPI, Path app = FastAPI() @app.get(“/hello/{name}”) async def hello(name:str=Path(…,min_length=3, max_length=10)): return {“name”: name} If the browser’s URL contains the parameter with a length less than 3 or more than 10, as in (http://localhost:8000/hello/Tutorialspoint), there will be an appropriate error message such as − { “detail”: [ { “loc”: [ “path”, “name” ], “msg”: “ensure this value has at most 10 characters”, “type”: “value_error.any_str.max_length”, “ctx”: { “limit_value”: 10 } } ] } The OpenAPI docs also shows the validations applied − Validation rules can be applied on numeric parameters too, using the operators as given below − gt − greater than ge − greater than or equal lt − less than le − less than or equal Let us modify the above operation decorator to include age as a path parameter and apply the validations. from fastapi import FastAPI, Path app = FastAPI() @app.get(“/hello/{name}/{age}”) async def hello(*, name: str=Path(…,min_length=3 , max_length=10), age: int = Path(…, ge=1, le=100)): return {“name”: name, “age”:age} In this case, validation rules are applied for both the parameters name and age. If the URL entered is http://localhost:8000/hello/hi/110, then the JSON response shows following explanations for validation failure − { “detail”: [ { “loc”: [ “path”, “name” ], “msg”: “ensure this value has at least 3 characters”, “type”: “value_error.any_str.min_length”, “ctx”: { “limit_value”: 3 } }, { “loc”: [ “path”, “age” ], “msg”: “ensure this value is less than or equal to 100”, “type”: “value_error.number.not_le”, “ctx”: { “limit_value”: 100 } } ] } The swagger UI documentation also identifies the constraints. The query parameters can also have the validation rules applied to them. You have to specify them as the part of arguments of Query class constructor. Let us add a query parameter called percent in the above function and apply the validation rules as ge=0 (i.e., greater then equal to 0) and lt=100 (less than or equal to 100) from fastapi import FastAPI, Path, Query @app.get(“/hello/{name}/{age}”) async def hello(*, name: str=Path(…,min_length=3 , max_length=10), age: int = Path(…, ge=1, le=100), percent:float=Query(…, ge=0, le=100)): return {“name”: name, “age”:age} If the URL entered is http://localhost:8000/hello/Ravi/20?percent=79, then the browser displays following JSON response − {“name”:”Ravi”,”age”:20} FastAPI correctly identifies percent as a query parameter with validation conditions applied. It is reflected in the OpenAPI documentation as follows − While the client can send the path and query parameters to the API server using GET method, we need to apply POST method to send some binary data as a part of the HTTP request. This binary data may be in the form of an object of any Python class. It forms the request body. FastAPI uses Pydantic library for this purpose. Print Page Previous Next Advertisements ”;

FastAPI – Introduction

FastAPI – Introduction ”; Previous Next FastAPI is a modern Python web framework, very efficient in building APIs. It is based on Python’s type hints feature that has been added since Python 3.6 onwards. It is one of the fastest web frameworks of Python. As it works on the functionality of Starlette and Pydantic libraries, its performance is amongst the best and on par with that of NodeJS and Go. In addition to offering high performance, FastAPI offers significant speed for development, reduces human-induced errors in the code, is easy to learn and is completely production-ready. FastAPI is fully compatible with well-known standards of APIs, namely OpenAPI and JSON schema. FastAPI has been developed by Sebastian Ramirez in Dec. 2018. FastAPI 0.68.0 is the currently available version. FastAPI – EnvironmentSetup To install FastAPI (preferably in a virtual environment), use pip installer. pip3 install fastapi FastAPI depends on Starlette and Pydantic libraries, hence they also get installed. Installing Uvicorn using PIP FastAPI doesn’t come with any built-in server application. To run FastAPI app, you need an ASGI server called uvicorn, so install the same too, using pip installer. It will also install uvicorn’s dependencies – asgiref, click, h11, and typing-extensions pip3 install uvicorn With these two libraries installed, we can check all the libraries installed so far. pip3 freeze asgiref==3.4.1 click==8.0.1 colorama==0.4.4 fastapi==0.68.0 h11==0.12.0 importlib-metadata==4.6.4 pydantic==1.8.2 starlette==0.14.2 typing-extensions==3.10.0.0 uvicorn==0.15.0 zipp==3.5.0 Print Page Previous Next Advertisements ”;

FastAPI – Type Hints

FastAPI – Type Hints ”; Previous Next FastAPI makes extensive use of the Type hinting feature made available in Python’s version 3.5 onwards. As a matter of fact, Python is known to be a dynamically typed language. It also happens to be Python’s distinct feature. In a Python code, a variable need not be declared to be belonging to a certain type, and its type is determined dynamically by the instantaneous value assigned to it. Python’s interpreter doesn’t perform type checks and hence it is prone to runtime exceptions. In the following example, a division() function is defined with two parameters and returns their division, assuming that the parameters will be numeric. >>> def division(a, b): return a/b >>> division(10, 4) 2.5 >>> division(10, 2.5) 4.0 However, if one of the values passed to the function happen to be nonnumeric, it results in TypeError as shown below − >>> division(“Python”,5) TypeError: unsupported operand type(s) for /: ”str” and ”int” Even a basic coding environment such as IDLE indicates that the function requires two parameters but won’t specify the types as they haven’t been declared. Python’s new type hinting feature helps in prompting the user with the expected type of the parameters to be passed. This is done by adding a colon and data type after the parameter. We’ll redefine the division() function as follows − Note that while calling the function, Python hints at the expected type of each parameter to be passed. However, this doesn’t prevent the TypeError from appearing if an incompatible value is passed. You will have to use a static type checker such as MyPy to check for compatibility before running. Just as the formal parameters in the function’s definition, it is possible to provide type hint for a function’s return value. Just before the colon symbol in the function’s definition statement (after which the function block starts) add an arrow (->) and the type. However, as mentioned earlier, if incompatible values are passed to the function, or returned by the function, Python reports TypeError. Use of MyPy static type checker can detect such errors. Install mypy package first. pip3 install mypy Save the following code as typecheck.py def division(x:int, y:int) -> int: return (x//y) a=division(10,2) print (a) b=division(5,2.5) print (b) c=division(“Hello”,10) print (c) Check this code for type errors using mypy. C:python37>mypy typechk.py typechk.py:7: error: Argument 2 to “division” has incompatible type “float”; expected “int” typechk.py:10: error: Argument 1 to “division” has incompatible type “str”; expected “int” Found 2 errors in 1 file (checked 1 source file) There are errors in second and third calls to the function. In second, value passed to y is float when int is expected. In third, value passed to x is str when int is expected. (Note that // operator returns integer division) All standard data types can be used as type hints. This can be done with global variables, variables as function parameters, inside function definition etc. x: int = 3 y: float = 3.14 nm: str = ”abc” married: bool = False names: list = [”a”, ”b”, ”c”] marks: tuple = (10, 20, 30) marklist: dict = {”a”: 10, ”b”: 20, ”c”: 30} A new addition in newer versions of Python (version 3.5 onwards) standard library is the typing module. It defines special types for corresponding standard collection types. The types on typing module are List, Tuple, Dict, and Sequence. It also consists of Union and Optional types. Note that standard names of data types are all in small case, whereas ones in typing module have first letter in upper case. Using this feature, we can ask a collection of a particular type. from typing import List, Tuple, Dict # following line declares a List object of strings. # If violated, mypy shows error cities: List[str] = [”Mumbai”, ”Delhi”, ”Chennai”] # This is Tuple with three elements respectively # of str, int and float type) employee: Tuple[str, int, float] = (”Ravi”, 25, 35000) # Similarly in the following Dict, the object key should be str # and value should be of int type, failing which # static type checker throws error marklist: Dict[str, int] = {”Ravi”: 61, ”Anil”: 72} Print Page Previous Next Advertisements ”;

FastAPI – Uvicorn

FastAPI – Uvicorn ”; Previous Next Unlike the Flask framework, FastAPI doesn’t contain any built-in development server. Hence we need Uvicorn. It implements ASGI standards and is lightning fast. ASGI stands for Asynchronous Server Gateway Interface. The WSGI (Web Server Gateway Interface – the older standard) compliant web servers are not suitable for asyncio applications. Python web frameworks (such as FastAPI) implementing ASGI specifications provide high speed performance, comparable to web apps built with Node and Go. Uvicorn uses uvloop and httptools libraries. It also provides support for HTTP/2 and WebSockets, which cannot be handled by WSGI. uvloop id similar to the built-in asyncio event loop. httptools library handles the http protocols. The installation of Uvicorn as described earlier will install it with minimal dependencies. However, standard installation will also install cython based dependencies along with other additional libraries. pip3 install uvicorn(standard) With this, the WebSockets protocol will be supported. Also, PyYAML will be installed to allow you to provide a .yaml file. As mentioned earlier, the application is launched on the Uvicorn server with the following command − uvicorn main:app –reload The –reload option enables the debug mode so that any changes in app.pywill be automatically reflected and the display on the client browser will be automatically refreshed. In addition, the following command-line options may be used − Sr.No Command & Description 1 –host TEXT Bind socket to this host. [default 127.0.0.1] 2 –port INTEGER Bind socket to this port. [default 8000] 3 –uds TEXT Bind to a UNIX domain socket. 4 –fd INTEGER Bind to socket from this file descriptor. 5 –reload Enable auto-reload. 6 –reload-dir PATH Set reload directories explicitly, default current working directory. 7 –reload-include TEXT Include files while watching. Includes ”*.py” by default 8 -reload-exclude TEXT Exclude while watching for files. 9 –reload-delay FLOAT Delay between previous and next check default 0.25 10 -loop [auto|asyncio|uvloop] Event loop implementation. [default auto] 11 –http [auto|h11|httptools] HTTP protocol implementation. [default auto] 12 –interface auto|asgi|asgi|wsgi Select application interface. [default auto] 13 –env-file PATH Environment configuration file. 14 –log-config PATH Logging configuration file. Supported formats .ini, .json, .yaml. 15 –version Display the uvicorn version and exit. 16 –app-dir TEXT Look for APP in the specified directory default current directory 17 –help Show this message and exit. Instead of starting Uvicorn server from command line, it can be launched programmatically also. Example In the Python code, call uvicorn.run() method, using any of the parameters listed above − import uvicorn from fastapi import FastAPI app = FastAPI() @app.get(“/”) async def index(): return {“message”: “Hello World”} if __name__ == “__main__”: uvicorn.run(“main:app”, host=”127.0.0.1″, port=8000, reload=True) Now run this app.py as Python script as follows − (fastapienv) C:fastapienv>python app.py Uvicorn server will thus be launched in debug mode. Print Page Previous Next Advertisements ”;

FastAPI – Pydantic

FastAPI – Pydantic ”; Previous Next Pydantic is a Python library for data parsing and validation. It uses the type hinting mechanism of the newer versions of Python (version 3.6 onwards) and validates the types during the runtime. Pydantic defines BaseModel class. It acts as the base class for creating user defined models. Following code defines a Student class as a model based on BaseModel. from typing import List from pydantic import BaseModel class Student(BaseModel): id: int name :str subjects: List[str] = [] The attributes of the Student class are declared with type hints. Note that the subjects attribute is of List type defined in typing module and of builtin list type. We can populate an object of Student class with a dictionary with matching structure as follows − >>> data = { ”id”: 1, ”name”: ”Ravikumar”, ”subjects”: [“Eng”, “Maths”, “Sci”], } >>> s1=Student(**data) >>> print (s1) id=1 name=”Ravikumar” subjects=[”Eng”, ”Maths”, ”Sci”] >>> s1 Student(id=1, name=”Ravikumar”, subjects=[”Eng”, ”Maths”, ”Sci”]) >>> s1.dict() {”id”: 1, ”name”: ”Ravikumar”, ”subjects”: [”Eng”, ”Maths”, ”Sci”]} Pydantic will automatically get the data types converted whenever possible. For example, even if the id key in the dictionary is assigned a string representation of a number (such as ”123”), it will coerce it into an integer. But whenever not possible, an exception will be raised. >>> data = { ”id”: [1,2], ”name”: ”Ravikumar”, ”subjects”: [“Eng”, “Maths”, “Sci”], } >>> s1=Student(**data) Traceback (most recent call last): File “<pyshell#13>”, line 1, in <module> s1=Student(**data) File “pydanticmain.py”, line 406, in pydantic.main.BaseModel.__init__ pydantic.error_wrappers.ValidationError: 1 validation error for Student id value is not a valid integer (type=type_error.integer) Pydantic also contains a Field class to declare metadata and validation rules for the model attributes. First modify the Student class to apply Field type on “name” attribute as follows − from typing import List from pydantic import BaseModel, Field class Student(BaseModel): id: int name :str = Field(None, title=”The description of the item”, max_length=10) subjects: List[str] = [] Populate the data as shown below. The name here is exceeding the max_length stipulated. Pydantic throws ValidationError as expected. >>> data = { ”id”: 1, ”name”: ”Ravikumar Sharma”, ”subjects”: [“Eng”, “Maths”, “Sci”], } >>> s1=Student(**data) Traceback (most recent call last): File “<pyshell#28>”, line 1, in <module> s1=Student(**data) File “pydanticmain.py”, line 406, in pydantic.main.BaseModel.__init__ pydantic.error_wrappers.ValidationError: 1 validation error for Student name ensure this value has at most 10 characters (type=value_error.any_str.max_length; limit_value=10) Pydantic models can be used to map with ORM models like SQLAlchemy or Peewee. Print Page Previous Next Advertisements ”;