REST API Generator¶
BESSER offers a code generator for REST API utilizing the FastAPI framework. This tool automatically transforms classes and relationships defined in a Structural model into a RESTful service.
Let’s generate the code for the REST model of our Structural model example structural model example.
You should create a RESTAPIGenerator object, provide the Structural model, and use the generate method as follows:
from besser.generators.rest_api import RESTAPIGenerator
rest_api = RESTAPIGenerator(model=library_model, http_methods=["GET", "POST", "PUT", "PATCH", "DELETE"], backend=False)
rest_api.generate()
Parameters¶
model: The input B-UML structural model (required).http_methods: List of HTTP methods to generate (default:["GET", "POST", "PUT", "PATCH", "DELETE"]).backend: WhenTrue, generatesmain_api.pysuitable for BackendGenerator integration instead of standalonerest_api.py(default:False).port: Port number for the generated FastAPI app (optional).output_dir: Output directory (default:output/in the current directory).
Note
REST API vs. Backend Generator: The REST API generator produces API endpoints and Pydantic models only. The Backend generator builds on top of this by also generating SQLAlchemy ORM models and database setup. Use the REST API generator when you only need API scaffolding; use the Backend generator when you need a complete backend with database.
Output Files¶
The generator creates two files in the output directory:
rest_api.py(ormain_api.pywhenbackend=True): FastAPI application with CRUD endpoints for each class.pydantic_classes.py: Pydantic validation models generated by the Pydantic generator.
The generated output for the library example is shown below.
1import os, json
2from fastapi import FastAPI, HTTPException
3from pydantic_classes import *
4
5app = FastAPI()
6
7############################################
8#
9# Lists to store the data (json)
10#
11############################################
12
13book_list = []
14author_list = []
15library_list = []
16
17
18############################################
19#
20# Book functions
21#
22############################################
23@app.get("/book/", response_model=List[Book], tags=["book"])
24def get_book():
25 return book_list
26
27@app.get("/book/{attribute_id}/", response_model=Book, tags=["book"])
28def get_book(attribute_id : str):
29 for book in book_list:
30 if book.id_to_change== attribute_id:
31 return book
32 raise HTTPException(status_code=404, detail="Book not found")
33
34@app.post("/book/", response_model=Book, tags=["book"])
35def create_book(book: Book):
36 book_list.append(book)
37 return book
38
39@app.put("/book/{attribute_id}/", response_model=Book, tags=["book"])
40def change_book(attribute_id : str, updated_book: Book):
41 for index, book in enumerate(book_list):
42 if book.id_to_change == attribute_id:
43 book_list[index] = updated_book
44 return updated_book
45 raise HTTPException(status_code=404, detail="Book not found")
46
47@app.patch("/book/{attribute_id}/{attribute_to_change}", response_model=Book, tags=["book"])
48def update_book(attribute_id : str, attribute_to_change: str, updated_data: str):
49 for book in book_list:
50 if book.id_to_change == attribute_id:
51 if hasattr(book, attribute_to_change):
52 setattr(book, attribute_to_change, updated_data)
53 return book
54 else:
55 raise HTTPException(status_code=400, detail=f"Attribute '{attribute_to_change}' does not exist")
56 raise HTTPException(status_code=404, detail="Book not found")
57
58@app.delete("/book/{attribute_id}/", tags=["book"])
59def delete_book(attribute_id : str):
60 for index, book in enumerate(book_list):
61 if book.id_to_change == attribute_id:
62 book_list.pop(index)
63 return {"message": "Item deleted successfully"}
64 raise HTTPException(status_code=404, detail="Book not found")
65
66############################################
67#
68# Author functions
69#
70############################################
71@app.get("/author/", response_model=List[Author], tags=["author"])
72def get_author():
73 return author_list
74
75@app.get("/author/{attribute_id}/", response_model=Author, tags=["author"])
76def get_author(attribute_id : str):
77 for author in author_list:
78 if author.id_to_change== attribute_id:
79 return author
80 raise HTTPException(status_code=404, detail="Author not found")
81
82@app.post("/author/", response_model=Author, tags=["author"])
83def create_author(author: Author):
84 author_list.append(author)
85 return author
86
87@app.put("/author/{attribute_id}/", response_model=Author, tags=["author"])
88def change_author(attribute_id : str, updated_author: Author):
89 for index, author in enumerate(author_list):
90 if author.id_to_change == attribute_id:
91 author_list[index] = updated_author
92 return updated_author
93 raise HTTPException(status_code=404, detail="Author not found")
94
95@app.patch("/author/{attribute_id}/{attribute_to_change}", response_model=Author, tags=["author"])
96def update_author(attribute_id : str, attribute_to_change: str, updated_data: str):
97 for author in author_list:
98 if author.id_to_change == attribute_id:
99 if hasattr(author, attribute_to_change):
100 setattr(author, attribute_to_change, updated_data)
101 return author
102 else:
103 raise HTTPException(status_code=400, detail=f"Attribute '{attribute_to_change}' does not exist")
104 raise HTTPException(status_code=404, detail="Author not found")
105
106@app.delete("/author/{attribute_id}/", tags=["author"])
107def delete_author(attribute_id : str):
108 for index, author in enumerate(author_list):
109 if author.id_to_change == attribute_id:
110 author_list.pop(index)
111 return {"message": "Item deleted successfully"}
112 raise HTTPException(status_code=404, detail="Author not found")
113
114############################################
115#
116# Library functions
117#
118############################################
119@app.get("/library/", response_model=List[Library], tags=["library"])
120def get_library():
121 return library_list
122
123@app.get("/library/{attribute_id}/", response_model=Library, tags=["library"])
124def get_library(attribute_id : str):
125 for library in library_list:
126 if library.id_to_change== attribute_id:
127 return library
128 raise HTTPException(status_code=404, detail="Library not found")
129
130@app.post("/library/", response_model=Library, tags=["library"])
131def create_library(library: Library):
132 library_list.append(library)
133 return library
134
135@app.put("/library/{attribute_id}/", response_model=Library, tags=["library"])
136def change_library(attribute_id : str, updated_library: Library):
137 for index, library in enumerate(library_list):
138 if library.id_to_change == attribute_id:
139 library_list[index] = updated_library
140 return updated_library
141 raise HTTPException(status_code=404, detail="Library not found")
142
143@app.patch("/library/{attribute_id}/{attribute_to_change}", response_model=Library, tags=["library"])
144def update_library(attribute_id : str, attribute_to_change: str, updated_data: str):
145 for library in library_list:
146 if library.id_to_change == attribute_id:
147 if hasattr(library, attribute_to_change):
148 setattr(library, attribute_to_change, updated_data)
149 return library
150 else:
151 raise HTTPException(status_code=400, detail=f"Attribute '{attribute_to_change}' does not exist")
152 raise HTTPException(status_code=404, detail="Library not found")
153
154@app.delete("/library/{attribute_id}/", tags=["library"])
155def delete_library(attribute_id : str):
156 for index, library in enumerate(library_list):
157 if library.id_to_change == attribute_id:
158 library_list.pop(index)
159 return {"message": "Item deleted successfully"}
160 raise HTTPException(status_code=404, detail="Library not found")
161
162
163
164############################################
165# Maintaining the server
166############################################
167if __name__ == "__main__":
168 import uvicorn
169 openapi_schema = app.openapi()
170 output_dir = os.path.join(os.getcwd(), 'output')
171 os.makedirs(output_dir, exist_ok=True)
172 output_file = os.path.join(output_dir, 'openapi_specs.json')
173 print(f"Writing OpenAPI schema to {output_file}")
174 with open(output_file, 'w') as file:
175 json.dump(openapi_schema, file)
176 uvicorn.run(app, host="0.0.0.0", port=8000)
177
178
179
When you run the code generated, the OpenAPI specifications will be generated:
{
"openapi": "3.1.0",
"info": {
"title": "FastAPI",
"version": "0.1.0"
},
"paths": {
"/author/": {
"get": {
"tags": [
"author"
],
"summary": "Get Author",
"operationId": "get_author_author__get",
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {
"items": {
"$ref": "#/components/schemas/Author"
},
"type": "array",
"title": "Response Get Author Author Get"
}}}}}}}}}
Note
The full OpenAPI specification is available at http://localhost:8000/docs
when you run the generated FastAPI application with uvicorn rest_api:app.