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 one include_router per resource). It keeps its historical filename and module-level app object, so uvicorn main_api:app works 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 the get_db dependency. The database defaults to sqlite:///./data/<model>.db and can be overridden with the DATABASE_URL environment variable (shared with sql_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).

Backend Generator user schema

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

/{entity}/

Get all entities (supports detailed=true for relationships)

GET

/{entity}/count/

Get total count of entities

GET

/{entity}/paginated/

Paginated list with skip, limit, detailed params

GET

/{entity}/search/

Search by attributes (auto-generated filters)

GET

/{entity}/{id}/

Get single entity by ID

POST

/{entity}/

Create single entity

POST

/{entity}/bulk/

Bulk create multiple entities

PUT

/{entity}/{id}/

Full update of entity

DELETE

/{entity}/{id}/

Delete single entity

DELETE

/{entity}/bulk/

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

/{entity}/{id}/{relationship}/

Get all related entities

POST

/{entity}/{id}/{relationship}/{related_id}/

Add a relationship

DELETE

/{entity}/{id}/{relationship}/{related_id}/

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

/{entity}/{id}/methods/{method_name}/

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

/{entity}/methods/{method_name}/

Execute class-level method

Supported Parameter Types:

The following parameter types are supported for method parameters:

  • str - String values

  • int - Integer values

  • float - Floating-point values

  • bool - Boolean values

  • date - Date values

  • datetime - DateTime values

  • time - 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 PyTorch NeuralNetwork class, as produced by the Pytorch Generator.

  • neural_networks/weights/: where you put the trained weights, <network>.pt (a state_dict saved with torch.save(model.state_dict(), ...)). The folder’s README.md lists the expected files.

  • nn_runtime.py: loads each network once, on first call, and runs it.

  • torch in requirements.txt (and in the Dockerfile when docker_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

Health check endpoint for monitoring

GET

/statistics

Database statistics (entity counts)