Backend Generator¶
BESSER provides a code generator for backend services, utilizing the FastAPI framework, SQL Alchemy for database interactions, and Pydantic for data validation. This tool enables developers to seamlessly transform classes and relationships defined in a B-UML model into a fully functional backend service.
The BESSER backend generator streamlines the development process by leveraging multiple specialized generators within the BESSER suite to automatically produce:
A RESTful API Service: Utilizes the BESSER REST API Generator to create dynamic, scalable API endpoints.
Database Models: Integrates BESSER’s SQL Alchemy Generator to construct efficient ORM-based models for database interactions.
Data Validation Models: Employs BESSER’s Pydantic Generator to ensure that data conforms to the defined schemas, enhancing the integrity and security of the backend.
See also
This generator emits a fixed scaffold. If you need more than the template provides — JWT authentication, a Dockerfile, migrations, tests — the Spec-Driven Agent runs this generator first and then lets an LLM customise its output to a natural-language request, validating and repairing the result before handing it back.
To generate the complete backend for a B-UML model, follow the steps below. The example uses the library example B-UML model as a reference.
from besser.generators.backend import BackendGenerator
backend = BackendGenerator(model=library_model, http_methods=['GET', 'POST', 'PUT', 'DELETE'], nested_creations = True, docker_image = True)
backend.generate()
You can customize the code generation patterns by selecting specific HTTP methods such as GET, POST, PUT, and DELETE.
This selection allows for targeted code generation tailored to only the necessary components of the API.
If specified in the B-UML model with read_only attributes, the generator will automatically exclude the PUT and DELETE methods.
Additionally, the nested_creations parameter configures how the API manages entity relationships in requests. If set to True, the API allows both
the linking of existing entities via identifiers and the nested creation of new entities. If set to False, the API only permits linking existing
entities using their identifiers. The default setting is False, which restricts the functionality to linking by identifiers. Finally, the
docker_image parameter is there to assist you in creating a Docker image for your application.
Invoke the generate method to produce the backend code.The generated files will be placed in the <<current_directory>>/output_backend.
This method will generate a modular project (rather than one large file) so each concern lives in its own module:
main_api.py: The slim FastAPI application entry point (app setup, middleware, exception handlers, system endpoints, and oneinclude_routerper resource). It keeps its historical filename and module-levelappobject, souvicorn main_api:appworks unchanged.
routers/<class>.py: One router module per class in the model, containing all of that class’s CRUD, relationship and method endpoints.
database.py: The shared engine/session setup and theget_dbdependency. The database defaults tosqlite:///./data/<model>.dband can be overridden with theDATABASE_URLenvironment variable (shared withsql_alchemy.py, so the ORM and the API always point at the same database).
bal_stdlib.py: The B-UML Action Language standard-library helpers plus the association-class link helpers, shared by the routers.
sql_alchemy.py: Includes SQL Alchemy database models.
pydantic_classes.py: Consists of Pydantic validation models.
requirements.txt: The dependencies of the generated application.
Running the application creates the SQLite database file under data/ (unless DATABASE_URL points elsewhere).
The REST API communicates with the database through SQLAlchemy, and Pydantic validates the data before it’s sent or after it’s received by the REST API. This setup encapsulates a modern backend architecture where each piece serves a specific role in data handling and processing. When you run the code generated, a SqlLite database and the OpenAPI specifications will be generated.
We have an example demonstrating how this generator works, which you can find here: Backend example. This example showcases the usage of the Backend Generator with our Structural model example Example, illustrating its application in generating a fully functional backend from a B-UML model.
Docker Image Generation¶
The Backend Generator offers the docker_image boolean parameter, designed to streamline the creation and uploading of Docker images for the generated backend. When
you set this parameter to True, you have two options for creating your image:
1. Automated DockerHub Integration¶
Our code generator can create and upload the Docker image to your DockerHub in one step. Provide a configuration file through the docker_config_path parameter, which
enables the generator to automatically create and upload the image in your DockerHub accout using the provided configurations.
To create the configuration file, use the following template and save it as a .conf file:
[DEFAULT]
docker_username = dockerhub_username
docker_password = dockerhub_password
docker_image_name = image_name
docker_repository = dockerhub_repository
docker_tag = image_tag
docker_port = port
2. Custom Dockerfile Generation¶
The second option is to generate the Dockerfile with the instructions to create your Docker image and then run it yourself to build the image and upload it to
your repository. To follow this option, just don’t use the docker_config_path parameter in the code generator. BESSER will then assume that you will handle
running the Dockerfile and uploading the image to the repository yourself.
The generator will create two files:
Dockerfile: Contains the necessary instructions to build the Docker image.create_docker_image.py: A Python script that automates the process of building and uploading the Docker image.
By providing this script and the Dockerfile, users can build and upload their Docker images by executing the script with their DockerHub credentials.
Warning
If you use the generator to generate and load the Docker image in DockerHub, you must make sure you have a Docker engine installed on your computer. For example Docker desktop.
Generated API Endpoints¶
The Backend Generator creates a comprehensive REST API with the following endpoint categories for each entity in your B-UML model:
CRUD Operations¶
Method |
Endpoint |
Description |
|---|---|---|
GET |
|
Get all entities (supports |
GET |
|
Get total count of entities |
GET |
|
Paginated list with |
GET |
|
Search by attributes (auto-generated filters) |
GET |
|
Get single entity by ID |
POST |
|
Create single entity |
POST |
|
Bulk create multiple entities |
PUT |
|
Full update of entity |
DELETE |
|
Delete single entity |
DELETE |
|
Bulk delete by IDs |
Changed in version 8.0.0: The request bodies of POST and PUT (the Pydantic <Class>Create
schemas) no longer accept fields the server owns: an attribute named id
that is not a declared primary key, the createdAt / updatedAt
timestamps, and attributes marked is_derived. A declared primary key
(is_id=True) is client-supplied and stays in the schema, so a model
whose id must come from the client should mark it is_id. On the
non-owning side of a one-to-one association the schema has no field for the
link; set it from the side that holds the foreign key. Relationship fields
are typed after the referenced primary key rather than always int.
A default_value must be expressible as a literal of the attribute’s
type; otherwise generation raises InvalidDefaultValueError. See the
release notes.
Relationship Management (N:M)¶
For many-to-many relationships, additional endpoints are generated:
Method |
Endpoint |
Description |
|---|---|---|
GET |
|
Get all related entities |
POST |
|
Add a relationship |
DELETE |
|
Remove a relationship |
Class Methods Endpoints¶
When your B-UML model includes methods defined on classes, the generator automatically creates API endpoints to execute them. The generator distinguishes between two types of methods based on the method signature:
Instance Methods (methods with self):
These methods operate on a specific entity instance. The endpoint requires an entity ID.
Method |
Endpoint |
Description |
|---|---|---|
POST |
|
Execute method on a specific entity instance |
Class Methods (methods without self):
These methods operate at the class level, performing operations on the entire collection or class-level logic.
Method |
Endpoint |
Description |
|---|---|---|
POST |
|
Execute class-level method |
Supported Parameter Types:
The following parameter types are supported for method parameters:
str- String valuesint- Integer valuesfloat- Floating-point valuesbool- Boolean valuesdate- Date valuesdatetime- DateTime valuestime- Time values
Request Format:
Parameters are passed as a JSON body:
{
"params": {
"param_name": "value",
"another_param": 42
}
}
Response Format:
The method execution returns a structured response:
{
"entity_id": 5,
"method": "method_name",
"status": "executed",
"result": "...",
"output": "captured print statements..."
}
Example - Instance Method:
If your B-UML model has a Book class with a method apply_discount(self, percent: float):
# B-UML method definition
def apply_discount(self, percent: float):
self.price = self.price * (1 - percent / 100)
The generator creates:
Endpoint:
POST /book/5/methods/apply_discount/Request body:
{"params": {"percent": 10}}
Example - Class Method:
If your B-UML model has a Book class with a method get_expensive_books(database, min_price: int):
# B-UML method definition (no self parameter)
def get_expensive_books(database, min_price: int):
return database.query(Book).filter(Book.price > min_price).all()
The generator creates:
Endpoint:
POST /book/methods/get_expensive_books/Request body:
{"params": {"min_price": 50}}
Note
The database parameter is automatically injected by the API framework and should not be passed in the request body.
Any print() statements executed during method execution are captured and returned in the output field of the response.
Methods Implemented by a Neural Network¶
A method whose implementation_type is NEURAL_NETWORK and whose neural_network points to an
NN model becomes a class-level endpoint that runs the network
(see Structural model for how to link one). The generator then also emits:
neural_networks/<network>.py: the PyTorchNeuralNetworkclass, as produced by the Pytorch Generator.neural_networks/weights/: where you put the trained weights,<network>.pt(astate_dictsaved withtorch.save(model.state_dict(), ...)). The folder’sREADME.mdlists the expected files.nn_runtime.py: loads each network once, on first call, and runs it.torchinrequirements.txt(and in theDockerfilewhendocker_image=True).
Calling the method runs the network on its parameters, in their declared order, as one input sample; the
batch dimension is added for you. A method with a single list parameter uses that list as the whole sample
(for example an image as nested lists). For a Patient class with score(height: float, weight: float):
Endpoint:
POST /patient/methods/score/Request body:
{"params": {"height": 1.7, "weight": 70.0}}Response
result:{"network": "scorer", "output": [0.42]}
Until the weights file exists, the endpoint answers 503; inputs that are not numbers, or that do not match
the network’s input shape, answer 422. Methods with the NEURAL_NETWORK type but no linked network
answer 501 like any other method without an implementation.
System Endpoints¶
The generated API also includes system-level endpoints:
Method |
Endpoint |
Description |
|---|---|---|
GET |
|
API information (name, version, status) |
GET |
|
Health check endpoint for monitoring |
GET |
|
Database statistics (entity counts) |