Difference between revisions of "FastAPI"
Jump to navigation
Jump to search
Line 129: | Line 129: | ||
Can use standard python types, or more funky python types like <code>datetime.timedelta</code> | Can use standard python types, or more funky python types like <code>datetime.timedelta</code> | ||
===Query=== | ===Query=== | ||
− | + | * Implicit way: params given in function args are by default query params. Can declare type and default value/optionality | |
− | |||
− | |||
* If you need additional validation, set arg default value as type fastapi.Query | * If you need additional validation, set arg default value as type fastapi.Query | ||
** min/max length | ** min/max length | ||
Line 145: | Line 143: | ||
** Can define list of multiple values as default | ** Can define list of multiple values as default | ||
===Path=== | ===Path=== | ||
− | * Path paramters are always required as it has to be part of the path | + | * Implicit way: Path paramters are always required as it has to be part of the path |
− | + | ** Just put the path parameter inside the curly braces on the path argument, e.g., "@app.get("/items/{item_id}")" | |
− | * Just put the path parameter inside the curly braces on the path argument, e.g., "@app.get("/items/{item_id}")" | + | * Explicit way: Include the parameter explicitly as a function argument of type <code>fastapi.Path</code>() |
− | |||
− | |||
* order of the different parameters doesn't matter | * order of the different parameters doesn't matter | ||
** Can have first argument of function by * to let python know that all arguments following it should be taken as kwargs, which will allow function arg reordering. | ** Can have first argument of function by * to let python know that all arguments following it should be taken as kwargs, which will allow function arg reordering. | ||
Line 155: | Line 151: | ||
** gt/lt, ge/le | ** gt/lt, ge/le | ||
===Body=== | ===Body=== | ||
− | |||
* subclass the <code>pydantic.BaseModel</code> object | * subclass the <code>pydantic.BaseModel</code> object | ||
* declare class attributes | * declare class attributes | ||
* In function args declare type as your new subclass | * In function args declare type as your new subclass | ||
− | |||
* <code>fastapi.Body</code> - Use to declare the entire unit of body params in the subclass to be optional, or to setup additional validation, metadata. | * <code>fastapi.Body</code> - Use to declare the entire unit of body params in the subclass to be optional, or to setup additional validation, metadata. | ||
** Additional optional args to Body: example | ** Additional optional args to Body: example | ||
Line 167: | Line 161: | ||
* include a member class Config within subclass class attributes | * include a member class Config within subclass class attributes | ||
** <code>schema_extra</code> to provide documentation example | ** <code>schema_extra</code> to provide documentation example | ||
+ | ===Form=== | ||
+ | * pass | ||
+ | ===File=== | ||
+ | * File inherits from Form | ||
+ | * Files are uploaded as "Form data". Form data has special encoding that;s different from JSON | ||
+ | ** Thus the header <code>multipart/form-data</code> | ||
+ | * UploadFile has following attributes | ||
+ | ** filename | ||
+ | ** content_type | ||
+ | ** file - an object with a file-like interface | ||
+ | ** async methods that need to be awaited if your function is async | ||
+ | *** contents = await myfile.read() | ||
+ | *** write, seek, and close | ||
+ | |||
==Response models== | ==Response models== | ||
Line 174: | Line 182: | ||
** Can dump contents of model A's data instance directly into the constructor for modelB's instance another using <code>ModelB( **model_a.dict() )</code>. The filtering will also happen at this step. | ** Can dump contents of model A's data instance directly into the constructor for modelB's instance another using <code>ModelB( **model_a.dict() )</code>. The filtering will also happen at this step. | ||
** Can have SUB-subclasses to reduce code duplication. | ** Can have SUB-subclasses to reduce code duplication. | ||
+ | |||
+ | ==Using the Request Directly (GET IP ADDRESS)== | ||
+ | * <code>fastapi.Request</code> is just alias to <code>starlette.Request</code> | ||
+ | * Include <code>request: Request</code> in the function args | ||
+ | * This is how you get access to unvarnished, unvalidated request as it comes in from client | ||
+ | * request attributes | ||
+ | ** request.method | ||
+ | ** request.url, request.url.path, request.url.port, request.url.scheme | ||
+ | ** request.headers['content-type'] (e.g.) | ||
+ | ** request.query_params['search'] (e.g.) | ||
+ | ** request.path_params['username'] (e.g.) | ||
+ | ** request.client, *HOSTNAME OR IP ADDRESS* <code>request.client.host</code>, | ||
+ | ** request.cookies.get('mycookie') | ||
+ | |||
+ | ==Background Tasks== | ||
+ | * Send email notifications, process data, write to logs, etc AFTER returning a response | ||
+ | # Import <code>fastapi.BackgroundTasks</code> | ||
+ | # Define a parameter <code>background_tasks</code> in the path operation function with type declaration of BackgroundTasks | ||
+ | # Use background_tasks.add_task( function, args, kwargs ) | ||
+ | * Use dependency injection to hang a background task on the queue anywhere on the call stack (not just within the path operation function) | ||
==Other stuff I need to learn== | ==Other stuff I need to learn== | ||
* <code>fastapi.Cookie</code> | * <code>fastapi.Cookie</code> | ||
* the role of header parameters | * the role of header parameters |
Revision as of 00:39, 27 April 2020
Contents
- 1 Why fastAPI
- 2 Technologies
- 3 URL mapping
- 4 Getting stuff from requests: Path variables
- 5 Getting stuff from URL requests: Query parameters
- 6 Data Validation: Pydantic
- 7 Extract blob of JSON that came in from request
- 8 Creating responses
- 9 Implied and explicit Query, Path and Body Parameters
- 10 Response models
- 11 Using the Request Directly (GET IP ADDRESS)
- 12 Background Tasks
- 13 Other stuff I need to learn
Why fastAPI
- a free UI to test your rest api - interactive API documentation systems, OpenAPI (Swagger) and JSON Schema standards
- Supposedly security and authentication is integrated
- Supposedly fast
- Editor support, useful for code editor autocompletion
Technologies
- REST
- how you form your API
- Representational State Transfer
- Has an HTTP verb, a URL which related to a bit of data, and a parameterization of that
- Async
- How you deliver it
- Co-operative multitasking
- Puts the onus on you to decide when the rest of your computer should be able to do stuff
- Easy to get wrong
- Easy to write code that will block the whole web application rather than make just one request slow
- Excels in that it's able to deal with thousands and thousands of network sockets
- Dependency Injection (DI)
URL mapping
app = FastAPI() @app.get( "/" ) def root( ... ): return {"greeting": "Hello world"} app.include_router( events.router, prefix="/events"...)
router = APIRouter() @router.post( "/", ..., status=201) def create_object( ... ): ...
Getting stuff from requests: Path variables
- given this info
GET /events/1234?detail=full
Authorization: Token ABCD1234
- implicit way
@router.get( "/event/{id}" ) def get_object( id: int ): ...
- more explicit way - make things be required
from fastapi import Path from pydantic import Required @router.get( "/event/{id}" ) def get_object( id: int = Path(Required) ): ...
Getting stuff from URL requests: Query parameters
- given this info
GET /search/?text=something
Authorization: Token ABCD1234
- implicit way
@router.get( "/search" ) def search( text: str = None, offset: int = 0, limit: int = 100 ): ...
- more explicit way - make things be required
from fastapi import Query @router.get( "/search" ) def search( text: str = Query( default=None, max_length=20 ), offset: int = Query( 0 ), limit: int = Query( 100 ) ):
Data Validation: Pydantic
- Is this valid data??
- Throws helpful error messages if the data isn't formatted correctly
- "This field is required, but you didn't specify it."
from datatime import date as DateType from enum import Enum from pydantic import BaseModel class Types( Enum ): done = 'DONE' cancelled = 'CANCELLED' class Event( BaseModel ): date: DateType type: Types text: str
Extract blob of JSON that came in from request
from pydantic import Required @router.post( "/events/" ) def create_object( event: Event = Required ): ...
Creating responses
- If you need to retun something, just return simple Python objects. The framework renders them to JSON for you
@app.get( "/" ) def root( ... ): return {"greeting": "Hello World" } @app.post( "/events/". response_model=Event, status_code=201 ) def create_object( event: Event = Required ): ... return { "date": "2019-06-02", "type": "DONE", "text": "some stuff got done" }
Implied and explicit Query, Path and Body Parameters
Can use standard python types, or more funky python types like datetime.timedelta
Query
- Implicit way: params given in function args are by default query params. Can declare type and default value/optionality
- If you need additional validation, set arg default value as type fastapi.Query
- min/max length
- regex
- Declare more metadata
- title
- description
- alias
- deprecated=True
- set default as first arg to fastapi.Query()
- make required by using elipsis as first arg
- Can allow user to reuse query key by declaring type as typing.List[<type>]
- Can define list of multiple values as default
Path
- Implicit way: Path paramters are always required as it has to be part of the path
- Just put the path parameter inside the curly braces on the path argument, e.g., "@app.get("/items/{item_id}")"
- Explicit way: Include the parameter explicitly as a function argument of type
fastapi.Path
() - order of the different parameters doesn't matter
- Can have first argument of function by * to let python know that all arguments following it should be taken as kwargs, which will allow function arg reordering.
- validations
- gt/lt, ge/le
Body
- subclass the
pydantic.BaseModel
object - declare class attributes
- In function args declare type as your new subclass
fastapi.Body
- Use to declare the entire unit of body params in the subclass to be optional, or to setup additional validation, metadata.- Additional optional args to Body: example
pydantic.Field
- Use Field for additional validation on individual members of the BaseModel subclass- Model attributes/validation/metadata declared via Field() function args include title, max_length, description, gt,
example="Foo"
, etc...
- Model attributes/validation/metadata declared via Field() function args include title, max_length, description, gt,
- can nest list of Body Parameters withing body parameters using typing.List, typing.Set (converts collections with duplicate data into set of unique items), typing.Dict[int, float] for example, etc...
- include a member class Config within subclass class attributes
schema_extra
to provide documentation example
Form
- pass
File
- File inherits from Form
- Files are uploaded as "Form data". Form data has special encoding that;s different from JSON
- Thus the header
multipart/form-data
- Thus the header
- UploadFile has following attributes
- filename
- content_type
- file - an object with a file-like interface
- async methods that need to be awaited if your function is async
- contents = await myfile.read()
- write, seek, and close
Response models
- Declare a response model for validation and documentation purposes. Pydantic will also filter out any members not declared in the model.
- Can also leave out unset params in response using arg
response_model_exclude_unset=True
- Can also leave out unset params in response using arg
- Typical to have multiple related models.
- Can dump contents of model A's data instance directly into the constructor for modelB's instance another using
ModelB( **model_a.dict() )
. The filtering will also happen at this step. - Can have SUB-subclasses to reduce code duplication.
- Can dump contents of model A's data instance directly into the constructor for modelB's instance another using
Using the Request Directly (GET IP ADDRESS)
fastapi.Request
is just alias tostarlette.Request
- Include
request: Request
in the function args - This is how you get access to unvarnished, unvalidated request as it comes in from client
- request attributes
- request.method
- request.url, request.url.path, request.url.port, request.url.scheme
- request.headers['content-type'] (e.g.)
- request.query_params['search'] (e.g.)
- request.path_params['username'] (e.g.)
- request.client, *HOSTNAME OR IP ADDRESS*
request.client.host
, - request.cookies.get('mycookie')
Background Tasks
- Send email notifications, process data, write to logs, etc AFTER returning a response
- Import
fastapi.BackgroundTasks
- Define a parameter
background_tasks
in the path operation function with type declaration of BackgroundTasks - Use background_tasks.add_task( function, args, kwargs )
- Use dependency injection to hang a background task on the queue anywhere on the call stack (not just within the path operation function)
Other stuff I need to learn
fastapi.Cookie
- the role of header parameters