# APIRouter
When the number of endpoints increases in your API, it might be useful to split these endpoints up into seperate files, grouping endpoints based on functionality, data source, etc.
Splitting up your endpoints into seperate files can be achieved using the APIRouter
class of FastAPI.
For the explanation about how this all works, we will start with the files you can find in the APIRouter example start (opens new window) repository.
In the main.py
file of the repository mentioned above, we can find 4 endpoints, of which 3 GET request endpoints and 1 POST request endpoint. In the example below, we will split up these endpoints into two files. One file containing all GET request endpoints and another file for our POST request endpoint.
Right now, our file structure is as follows:
📂myproject
├── 📄.env (you should create the .env file yourself, it is not included in the repository)
├── 📄config.py
├── 📄database.py
├── 📄main.py
├── 📂models
| └── 📄models.py
└── 📂queries
└── 📄festival_queries.py
2
3
4
5
6
7
8
9
After implementing the APIRouter
to split up our endpoints into multiple files, our file structure will look like this:
📂myproject
├── 📄.env
├── 📄config.py
├── 📄database.py
├── 📄main.py
├── 📂models
| └── 📄models.py
├── 📂queries
| └── 📄festival_queries.py
└── 📂routes
├── 📄get_endpoints.py
└── 📄post_endpoints.py
2
3
4
5
6
7
8
9
10
11
12
# Creating files in the routes
folder
Let's make the necessary changes and add a routes
folder and the files get_endpoints.py
and post_endpoints.py
in it. As their names already suggest, they will contain either GET or POST requests. Please note that this is an example to demonstrate how endpoints can be spread over multiple files. You don't necessarily need to devide them based on the request type, you might as well split them up based on their functionality for example.
The basic structure of these files will be nearly the same as our current main.py
file, with the main difference being that we will not create a FastAPI
instance anymore. Instead, we will use an APIRouter
instance.
The file get_endpoints.py
will contain the following code:
from fastapi import APIRouter
import database
from queries import festival_queries as queries
app = APIRouter()
@app.get("/festivals")
def get_all_festivals():
query = queries.festival_name_query
festivals = database.execute_sql_query(query)
if isinstance(festivals, Exception):
return festivals, 500
festivals_to_return = []
for festival in festivals:
festivals_to_return.append(festival[0])
return({'festivals': festivals_to_return})
@app.get("/province")
def get_all_festivals_by_province(name: str):
query = queries.festivals_by_province_query
festivals = database.execute_sql_query(query, (
name,
))
if isinstance(festivals, Exception):
return festivals, 500
festivals_to_return = []
for festival in festivals:
festivals_to_return.append(festival[0])
return({'festivals': festivals_to_return})
@app.get("/name_and_month")
def get_all_festivals_by_name_and_month(name: str, month: int):
query = queries.festivals_by_name_and_month_query
festivals = database.execute_sql_query(query, (
'%{}%'.format(name),
month,
month,
))
if isinstance(festivals, Exception):
return festivals, 500
festivals_to_return = []
for festival in festivals:
location = festival[1] + ' (' + festival[4] + ')'
festival_dictionary = {"name": festival[0],
"startDate": festival[2],
"endDate": festival[3],
"location": location }
festivals_to_return.append(festival_dictionary)
return({'festivals': festivals_to_return})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
We have basically cut the GET endpoints and imports from main.py
and pasted them in get_endpoints.py
. An important difference however is that we worked with the APIRouter
class instead of the FastAPI
class, as highlighted in the code above.
We will do the same for the remaining POST request and paste it in post_endpoints.py
, just like the necessary imports:
from fastapi import APIRouter
import database
from models import models
from queries import festival_queries as queries
app = APIRouter()
@app.post("/festival")
def create_festival(festival: models.Festival):
query = queries.insert_festival_query
success = database.execute_sql_query(query, (
festival.name,
festival.location,
festival.startDate,
festival.endDate,
festival.province,
festival.comment,
))
if success:
return festival
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# Including the files with endpoints in main.py
In main.py
we now need to indicate that the endpoints of the files get_endpoints.py
and post_endpoints.py
should be part of our API. We can do this using the include_router()
function. As value of the router
parameter of this function, we should pass the APIRouter
instance of the file containing our endpoints. When doing so, don't forget to first import this file (or files in this case).
This is how we do it:
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
import config
from routes import get_endpoints, post_endpoints
app = FastAPI(docs_url=config.documentation_url)
app.include_router(router=get_endpoints.app)
app.include_router(router=post_endpoints.app)
origins = config.cors_origins.split(",")
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Add a prefix
If desired, it is possible to add a prefix to the endpoints coming from a certain file. If you want to add a prefix, add the prefix
parameter to the include_router()
function as shown below.
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
import config
from routes import get_endpoints, post_endpoints
app = FastAPI(docs_url=config.documentation_url)
app.include_router(router=get_endpoints.app, prefix="/get")
app.include_router(router=post_endpoints.app, prefix="/post")
origins = config.cors_origins.split(",")
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
The result of this addition is that the /festivals
endpoint in get_endpoints.py
should now be addressed as follows:
Instead of using the 'normal' URL without prefix: