FastAPI – Uploading Files ”; Previous Next First of all, to send a file to the server you need to use the HTML form’s enctype as multipart/form-data, and use the input type as the file to render a button, which when clicked allows you to select a file from the file system. <html> <body> <form action=”http://localhost:8000/uploader” method=”POST” enctype=”multipart/form-data”> <input type=”file” name=”file” /> <input type=”submit”/> </form> </body> </html> Note that the form’s action parameter to the endpoint http://localhost:8000/uploader and the method is set to POST. This HTML form is rendered as a template with following code − from fastapi import FastAPI, File, UploadFile, Request import uvicorn import shutil from fastapi.responses import HTMLResponse from fastapi.templating import Jinja2Templates app = FastAPI() templates = Jinja2Templates(directory=”templates”) @app.get(“/upload/”, response_class=HTMLResponse) async def upload(request: Request): return templates.TemplateResponse(“uploadfile.html”, {“request”: request}) Visit http://localhost:8000/upload/. You should get the form with Choose File button. Click it to open the file to be uploaded. The upload operation is handled by UploadFile function in FastAPI from fastapi import FastAPI, File, UploadFile import shutil @app.post(“/uploader/”) async def create_upload_file(file: UploadFile = File(…)): with open(“destination.png”, “wb”) as buffer: shutil.copyfileobj(file.file, buffer) return {“filename”: file.filename} We shall use shutil library in Python to copy the received file in the server location by the name destination.png Print Page Previous Next Advertisements ”;
Category: fastapi
FastAPI – Request Body
FastAPI – Request Body ”; Previous Next We shall now use the Pydantic model object as a request body of the client’s request. As mentioned earlier, we need to use POST operation decorator for the purpose. import uvicorn from fastapi import FastAPI from typing import List from pydantic import BaseModel, Field app = FastAPI() class Student(BaseModel): id: int name :str = Field(None, title=”name of student”, max_length=10) subjects: List[str] = [] @app.post(“/students/”) async def student_data(s1: Student): return s1 As it can be seen, the student_data() function is decorated by @app.post() decorator having the URL endpoint as “/students/”. It receives an object of Student class as Body parameter from the client’s request. To test this route, start the Uvicorn server and open the Swagger UI documentation in the browser by visiting http://localhost:8000/docs The documentation identifies that “/students/” route is attached with student_data() function with POST method. Under the schemas section the Student model will be listed. Expand the node in front of it to reveal the structure of the model Click the Try it out button to fill in the test values in the request body. Click the Execute button and get the server’s response values. While a Pydantic model automatically populates the request body, it is also possible to use singular values to add attributes to it. For that purpose, we need to use Body class objects as the parameters of the operation function to be decorated. First, we need to import Body class from fastapi. As shown in the following example, declare ”name” and ”marks” as the Body parameters in the definition of student_data() function below the @app.post() decorator. import uvicorn from fastapi import FastAPI, Body @app.post(“/students”) async def student_data(name:str=Body(…), marks:int=Body(…)): return {“name”:name,”marks”: marks} If we check the Swagger UI documentation, we should be able to find this POST method associated to student_data() function and having a request body with two parameters. It is also possible to declare an operation function to have path and/or query parameters along with request body. Let us modify the student_data() function to have a path parameter ”college’, ”age” as query parameter and a Student model object as body parameter. @app.post(“/students/{college}”) async def student_data(college:str, age:int, student:Student): retval={“college”:college, “age”:age, **student.dict()} return retval The function adds values of college and age parameters along with the dictionary representation of Student object and returns it as a response. We can check the API documentation as follows − As it can be seen, college is the path parameter, age is a query parameter, and the Student model is the request body. Print Page Previous Next Advertisements ”;
FastAPI – Mounting A Sub-App
FastAPI – Mounting a Sub-App ”; Previous Next If you have two independent FastAPI apps, one of them can be mounted on top of the other. The one that is mounted is called a sub-application. The app.mount() method adds another completely “independent” application in a specific path of the main app. It then takes care of handling everything under that path, with the path operations declared in that sub-application. Let us first declare a simple FastAPI application object to be used as a top level application. from fastapi import FastAPI app = FastAPI() @app.get(“/app”) def mainindex(): return {“message”: “Hello World from Top level app”} Then create another application object subapp and add its own path operations. subapp = FastAPI() @subapp.get(“/sub”) def subindex(): return {“message”: “Hello World from sub app”} Mount this subapp object on the main app by using mount() method. Two parameters needed are the URL route and name of the sub application. app.mount(“/subapp”, subapp) Both the main and sub application will have its own docs as can be inspected using Swagger UI. The sub application’s docs are available at http://localhost:8000/subapp/docs Print Page Previous Next Advertisements ”;
FastAPI – SQL Databases
FastAPI – SQL Databases ”; Previous Next In the previous chapter, a Python list has been used as an in-memory database to perform CRUD operations using FastAPI. Instead, we can use any relational database (such as MySQL, Oracle, etc.) to perform store, retrieve, update and delete operations. Instead of using a DB-API compliant database driver, we shall use SQLAlchemy as an interface between Python code and a database (we are going to use SQLite database as Python has in-built support for it). SQLAlchemy is a popular SQL toolkit and Object Relational Mapper. Object Relational Mapping is a programming technique for converting data between incompatible type systems in object-oriented programming languages. Usually, the type system used in an Object-Oriented language like Python contains non-scalar types. However, data types in most of the database products such as Oracle, MySQL, etc., are of primitive types such as integers and strings. In an ORM system, each class maps to a table in the underlying database. Instead of writing tedious database interfacing code yourself, an ORM takes care of these issues for you while you can focus on programming the logics of the system. In order to use SQLAlchemy, we need to first install the library using the PIP installer. pip install sqlalchemy SQLAlchemy is designed to operate with a DBAPI implementation built for a particular database. It uses dialect system to communicate with various types of DBAPI implementations and databases. All dialects require that an appropriate DBAPI driver is installed. The following are the dialects included − Firebird Microsoft SQL Server MySQL Oracle PostgreSQL SQLite Sybase Since we are going to use SQLite database, we need to create a database engine for our database called test.db. Import create_engine() function from sqlalchemy module. from sqlalchemy import create_engine from sqlalchemy.dialects.sqlite import * SQLALCHEMY_DATABASE_URL = “sqlite:///./test.db” engine = create_engine(SQLALCHEMY_DATABASE_URL, connect_args = {“check_same_thread”: False}) In order to interact with the database, we need to obtain its handle. A session object is the handle to database. Session class is defined using sessionmaker() − a configurable session factory method which is bound to the engine object. from sqlalchemy.orm import sessionmaker, Session session = sessionmaker(autocommit=False, autoflush=False, bind=engine) Next, we need a declarative base class that stores a catalog of classes and mapped tables in the Declarative system. from sqlalchemy.ext.declarative import declarative_base Base = declarative_base() Books, a subclass of Base, is mapped to a book table in the database. Attributes in the Books class correspond to the data types of the columns in the target table. Note that the id attribute corresponds to the primary key in the book table. from sqlalchemy import Column, Integer, String class Books(Base): __tablename__ = ”book” id = Column(Integer, primary_key=True, nullable=False) title = Column(String(50), unique=True) author = Column(String(50)) publisher = Column(String(50)) Base.metadata.create_all(bind=engine) The create_all() method creates the corresponding tables in the database. We now have to declare a Pydantic model that corresponds to the declarative base subclass (Books class defined above). from typing import List from pydantic import BaseModel, constr class Book(BaseModel): id: int title: str author:str publisher: str class Config: orm_mode = True Note the use of orm_mode=True in the config class indicating that it is mapped with the ORM class of SQLAlchemy. Rest of the code is just similar to in-memory CRUD operations, with the difference being the operation functions interact with the database through SQLalchemy interface. The POST operation on the FastAPI application object is defined below − from fastapi import FastAPI, Depends app=FastAPI() def get_db(): db = session() try: yield db finally: db.close() @app.post(”/add_new”, response_model=Book) def add_book(b1: Book, db: Session = Depends(get_db)): bk=Books(id=b1.id, title=b1.title, author=b1.author, publisher=b1.publisher) db.add(bk) db.commit() db.refresh(bk) return Books(**b1.dict()) A database session is first established. Data from the POST request body is added to the book table as a new row. Execute the add_book() operation function to add sample data to the books table. To verify, you can use SQLiteStudio, a GUI tool for SQLite databases. Two operation functions for GET operation are defined, one for fetching all the records, and one for the record matching a path parameter. Following is the get_books() function bound to the /list route. When executed, its server response is the list of all records. @app.get(”/list”, response_model=List[Book]) def get_books(db: Session = Depends(get_db)): recs = db.query(Books).all() return recs The /book/{id} route calls the get_book() function with id as path parameter. The SQLAlchemy’s query returns an object corresponding to the given id. @app.get(”/book/{id}”, response_model=Book) def get_book(id:int, db: Session = Depends(get_db)): return db.query(Books).filter(Books.id == id).first() The following image shows the result of get_books() function executed from the Swagger UI. The update and delete operations are performed by update_book() function (executed when /update/{id} route is visited) and del_book() function called when the route /delete/{id} is given in as the URL. @app.put(”/update/{id}”, response_model=Book) def update_book(id:int, book:Book, db: Session = Depends(get_db)): b1 = db.query(Books).filter(Books.id == id).first() b1.id=book.id b1.title=book.title b1.author=book.author b1.publisher=book.publisher db.commit() return db.query(Books).filter(Books.id == id).first() @app.delete(”/delete/{id}”) def del_book(id:int, db: Session = Depends(get_db)): try: db.query(Books).filter(Books.id == id).delete() db.commit() except Exception as e: raise Exception(e) return {“delete status”: “success”} If you intend to use any other database in place of SQLite, you need to only the change the dialect definition accordingly. For example, to use MySQL database and pymysql driver, change the statement of engine object to the following − engine = create_engine(”mysql+pymysql://user:password@localhost/test”) Print Page Previous Next Advertisements ”;
FastAPI – Websockets
FastAPI – Websockets ”; Previous Next A WebSocket is a persistent connection between a client and server to provide bidirectional, full-duplex communication between the two. The communication takes place over HTTP through a single TCP/IP socket connection. It can be seen as an upgrade of HTTP instead of a protocol itself. One of the limitations of HTTP is that it is a strictly half-duplex or unidirectional protocol. With WebSockets, on the other hand, we can send message-based data, similar to UDP, but with the reliability of TCP. WebSocket uses HTTP as the initial transport mechanism, but keeps the TCP connection alive the connection after the HTTP response is received. Same connection object it can be used two-way communication between client and server. Thus, real-time applications can be built using WebSocket APIs. FastAPI supports WebSockets through WebSocket class in FastAPI module. Following example demonstrates functioning of WebSocket in FastAPI application. First we have an index() function that renders a template (socket.html). It is bound to “/” route. The HTML file socket.html is placed in the “templates” folder. main.py from fastapi import FastAPI, Request from fastapi.responses import HTMLResponse from fastapi.templating import Jinja2Templates templates = Jinja2Templates(directory=”templates”) from fastapi.staticfiles import StaticFiles app = FastAPI() app.mount(“/static”, StaticFiles(directory=”static”), name=”static”) @app.get(“/”, response_class=HTMLResponse) async def index(request: Request): return templates.TemplateResponse(“socket.html”, {“request”: request}) The template file renders a text box and a button. socket.html <!DOCTYPE html> <html> <head> <title>Chat</title> <script src=”{{ url_for(”static”, path=”ws.js”) }}”></script> </head> <body> <h1>WebSocket Chat</h1> <form action=”” onsubmit=”sendMessage(event)”> <input type=”text” id=”messageText” autocomplete=”off”/> <button>Send</button> </form> <ul id=”messages”> </ul> </body> </html> Inside the socket.html, there is a call to the JavaScript function to be executed on the form’s submit. Hence, to serve JavaScript, the “static” folder is first mounted. The JavaScript file ws.js is placed in the “static” folder. ws.js var ws = new WebSocket(“ws://localhost:8000/ws”); ws.onmessage = function(event) { var messages = document.getElementById(”messages”) var message = document.createElement(”li”) var content = document.createTextNode(event.data) message.appendChild(content) messages.appendChild(message) }; function sendMessage(event) { var input = document.getElementById(“messageText”) ws.send(input.value) input.value = ”” event.preventDefault() } As the JavaScript code is loaded, it creates a websocket listening at “ws://localhost:8000/ws”. The sendMessage() function directs the input message to the WebSocket URL. This route invokes the websocket_endpoint() function in the application code. The incoming connection request is accepted and the incoming message is echoed on the client browser. Add the below code to main.py. from fastapi import WebSocket @app.websocket(“/ws”) async def websocket_endpoint(websocket: WebSocket): await websocket.accept() while True: data = await websocket.receive_text() await websocket.send_text(f”Message text was: {data}”) Save the FastAPI code file (main.py), template (socket.html) and JavaScript file (ws.js). Run the Uvicorn server and visit http://localhost:8000/ to render the chat window as below − Type a certain text and press Send button. The input message will be redirected on the browser through the websocket. Print Page Previous Next Advertisements ”;
FastAPI – Dependencies
FastAPI – Dependencies ”; Previous Next The built-in dependency injection system of FastAPI makes it possible to integrate components easier when building your API. In programming, Dependency injection refers to the mechanism where an object receives other objects that it depends on. The other objects are called dependencies. Dependency injection has the following advantages − reuse the same shared logic share database connections enforce authentication and security features Assuming that a FastAPI app has two operation functions both having the same query parameters id, name and age. from fastapi import FastAPI app = FastAPI() @app.get(“/user/”) async def user(id: str, name: str, age: int): return {“id”: id, “name”: name, “age”: age} @app.get(“/admin/”) async def admin(id: str, name: str, age: int): return {“id”: id, “name”: name, “age”: age} In case of any changes such as adding/removing query parameters, both the route decorators and functions need to be changed. FastAPI provides Depends class and its object is used as a common parameter in such cases. First import Depends from FastAPI and define a function to receive these parameters − async def dependency(id: str, name: str, age: int): return {“id”: id, “name”: name, “age”: age} Now we can use the return value of this function as a parameter in operation functions @app.get(“/user/”) async def user(dep: dict = Depends(dependency)): return dep For each new Request, FastAPI calls the dependency function using the corresponding parameters, returns the result, and assigns the result to your operation. You can use a class for managing dependencies instead of a function. Declare a class with id, name and age as attributes. class dependency: def __init__(self, id: str, name: str, age: int): self.id = id self.name = name self.age = age Use this class as the type of parameters. @app.get(“/user/”) async def user(dep: dependency = Depends(dependency)): return dep @app.get(“/admin/”) async def admin(dep: dependency = Depends(dependency)): return dep Here, we used the dependency injection in the operation function. It can also be used as operation decoration. For example, we want to check if the value of query parameter age is less than 21. If yes it should throw an exception. So, we write a function to check it and use it as a dependency. async def validate(dep: dependency = Depends(dependency)): if dep.age > 18: raise HTTPException(status_code=400, detail=”You are not eligible”) @app.get(“/user/”, dependencies=[Depends(validate)]) async def user(): return {“message”: “You are eligible”} In FastAPI dependency management, you can use yield instead of return to add some extra steps. For example, the following function uses database dependency with yield. async def get_db(): db = DBSession() try: yield db finally: db.close() Print Page Previous Next Advertisements ”;
FastAPI – HTML Form Templates ”; Previous Next Let us add another route “/login” to our application which renders a html template having a simple login form. The HTML code for login page is as follows − <html> <body> <form action=”/submit” method=”POST”> <h3>Enter User name</h3> <p><input type=”text” name=”nm”/></p> <h3>Enter Password</h3> <p><input type=”password” name=”pwd”/></p> <p><input type=”submit” value=”Login”/></p> </form> </body> </html> Note that the action parameter is set to “/submit” route and action set to POST. This will be significant for further discussion. Add login() function in the main.py file as under − @app.get(“/login/”, response_class=HTMLResponse) async def login(request: Request): return templates.TemplateResponse(“login.html”, {“request”: request}) The URL http://localhost:8000/login will render the login form as follows − Print Page Previous Next Advertisements ”;
FastAPI – CORS
FastAPI – CORS ”; Previous Next Cross-Origin Resource Sharing (CORS) is a situation when a frontend application that is running on one client browser tries to communicate with a backend through JavaScript code, and the backend is in a different “origin” than the frontend. The origin here is a combination of protocol, domain name, and port numbers. As a result, http://localhost and https://localhost have different origins. If the browser with a URL of one origin sends a request for the execution of JavaScript code from another origin, the browser sends an OPTIONS HTTP request. If the backend authorizes the communication from this different origin by sending the appropriate headers it will let the JavaScript in the frontend send its request to the backend. For that, the backend must have a list of “allowed origins”. To specify explicitly the allowed origins, import CORSMiddleware and add the list of origins to the app”s middleware. from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware app = FastAPI() origins = [ “http://192.168.211.:8000”, “http://localhost”, “http://localhost:8080”, ] app.add_middleware( CORSMiddleware, allow_origins=origins, allow_credentials=True, allow_methods=[“*”], allow_headers=[“*”], ) @app.get(“/”) async def main(): return {“message”: “Hello World”} Print Page Previous Next Advertisements ”;
FastAPI – Static Files
FastAPI – Static Files ”; Previous Next Often it is required to include in the template response some resources that remain unchanged even if there is a certain dynamic data. Such resources are called static assets. Media files (.png, .jpg etc), JavaScript files to be used for executing some front end code, or stylesheets for formatting HTML (.CSS files) are the examples of static files. In order to handle static files, you need a library called aiofiles pip3 install aiofiles Next, import StaticFiles class from the fastapi.staticfiles module. Its object is one of the parameters for the mount() method of the FastAPI application object to assign “static” subfolder in the current application folder to store and serve all the static assets of the application. app.mount(app.mount(“/static”, StaticFiles(directory=”static”), name=”static”) Example In the following example, FastAPI logo is to be rendered in the hello.html template. Hence, “fa-logo.png” file is first placed in static folder. It is now available for using as src attribute of <img> tag in HTML code. from fastapi import FastAPI, Request from fastapi.responses import HTMLResponse from fastapi.templating import Jinja2Templates from fastapi.staticfiles import StaticFiles app = FastAPI() templates = Jinja2Templates(directory=”templates”) app.mount(“/static”, StaticFiles(directory=”static”), name=”static”) @app.get(“/hello/{name}”, response_class=HTMLResponse) async def hello(request: Request, name:str): return templates.TemplateResponse(“hello.html”, {“request”: request, “name”:name}) The HTML code of templateshello.html is as follows − <html> <body> <h2>Hello {{name}} Welcome to FastAPI</h2> <img src=”{{ url_for(”static”, path=”fa-logo.png”) }}” alt=”” width=”300″> </body> </html> </pre> Run the Uvicorn server and visit the URL as http://localhost/hello/Vijay. The Logo appears in the browser window as shown. Example Here is another example of a static file. A JavaScript code hello.js contains a definition of myfunction() to be executed on the onload event in following HTML script (templateshello.html) <html> <head> <title>My Website</title> <script src=”{{ url_for(”static”, path=”hello.js”) }}”></script> </head> <body onload=”myFunction()”> <div id=”time” style=”text-align:right; width=”100%”></div> <h1><div id=”ttl”>{{ name }}</div></h1> </body> </html> The hello.js code is as follows − (statichello.js) function myFunction() { var today = new Date(); var h = today.getHours(); var m = today.getMinutes(); var s = today.getSeconds(); var msg=””; if (h<12) { msg=”Good Morning, “; } if (h>=12 && h<18) { msg=”Good Afternoon, “; } if (h>=18) { msg=”Good Evening, “; } var x=document.getElementById(”ttl”).innerHTML; document.getElementById(”ttl”).innerHTML = msg+x; document.getElementById(”time”).innerHTML = h + “:” + m + “:” + s; } The function detects the value of current time and assigns appropriate value to msg variable (good morning, good afternoon or good evening) depending on the time of the day. Save /static/hello.js, modify templateshello.html and restart the server. The browser should show the current time and corresponding message below it. Print Page Previous Next Advertisements ”;
FastAPI – Templates
FastAPI – Templates ”; Previous Next By default, FastAPI renders a JSON response to the client. However, it can be cast to a HTML response. For this purpose, FastAPI has HTMLResponse class defined in fastapi.responses module. We need to add response_class as an additional parameter to operation decorator, with HTMLResponse object as its value. In the following example, the @app.get() decorator has “/hello/” endpoint and the HTMLResponse as response_class. Inside the hello() function, we have a string representation of a HTML code of Hello World message. The string is returned in the form of HTML response. from fastapi.responses import HTMLResponse from fastapi import FastAPI app = FastAPI() @app.get(“/hello/”) async def hello(): ret=””” <html> <body> <h2>Hello World!</h2> </body> </html> ””” return HTMLResponse(content=ret) On examining the API docs, it can be seen that the server’s response body is in HTML. The request URL (http://localhost:8000/hello/) should also render the message in the browser. However, rendering a raw HTML response is very tedious. Alternately, it is possible to render prebuilt HTML pages as templates. For that we need to use a web template library. Web template library has a template engine that merges a static web page having place holder variables. Data from any source such as database is merged to dynamically generate and render the web page. FastAPI doesn’t have any prepackaged template library. So one is free to use any one that suits his needs. In this tutorial, we shall be using jinja2, a very popular web template library. Let us install it first using pip installer. pip3 install jinja2 FastAPI’s support for Jinja templates comes in the form of jinja2Templates class defined in fastapi.templates module. from fastapi.templating import Jinja2Templates To declare a template object, the folder in which the html templates are stored, should be provided as parameter. Inside the current working directory, we shall create a ‘templates’ directory. templates = Jinja2Templates(directory=”templates”) A simple web page ‘hello.html’ to render Hello World message is also put in ‘templates’ folder. <html> <body> <h2>Hello World!</h2> </body> </html> We are now going to render html code from this page as HTMLResponse. Let us modify the hello() function as follows − from fastapi.responses import HTMLResponse from fastapi.templating import Jinja2Templates from fastapi import FastAPI, Request app = FastAPI() templates = Jinja2Templates(directory=”templates”) @app.get(“/hello/”, response_class=HTMLResponse) async def hello(request: Request): return templates.TemplateResponse(“hello.html”, {“request”: request}) Here, templateResponse() method of template object collects the template code and the request context to render the http response. When we start the server and visit the http://localhost:8000/hello/ URL, we get to see the Hello World message in the browser, which is in fact the output of hello.html As mentioned earlier, jinja2 template allows certain place holders to be embedded in the HTML code. The jinja2 code elements are put inside the curly brackets. As soon as the HTML parser of the browser encounters this, the template engine takes over and populates these code elements by the variable data provided by the HTTP response. Jinja2 provides following code elements − {% %} – Statements {{ }} – Expressions to print to the template output {# #} − Comments which are not included in the template output # # # − Line statements The hello.html is modified as below to display a dynamic message by substituting the name parameter. <html> <body> <h2>Hello {{name}} Welcome to FastAPI</h2> </body> </html> The operation function hello() is also modified to accept name as a path parameter. The TemplateResponse should also include the JSON representation of “name”:name along with the request context. from fastapi.responses import HTMLResponse from fastapi.templating import Jinja2Templates from fastapi import FastAPI, Request app = FastAPI() templates = Jinja2Templates(directory=”templates”) @app.get(“/hello/{name}”, response_class=HTMLResponse) async def hello(request: Request, name:str): return templates.TemplateResponse(“hello.html”, {“request”: request, “name”:name}) Restart the server and go to http://localhost:8000/hello/Kiran. The browser now fills the jinja2 place holder with the path parameter in this URL. Print Page Previous Next Advertisements ”;