Home
Docs have been moved to GitHub pages, click here
This page describes key technical background on using API Logic Server. These procedures presume that you have performed the following:
-
Installed ApiLogicServer
-
Run the Quick Start to familiarize yourself with how to create, run and debug projects
You will typically want to customize and extend the created project. Edit the files described in the subsections below.
The 2 indicated files in the tree are the Python files that run for the Basic Web App and the API Server.
Projects are created from a system-supplied prototype. You can use your own prototype from git (or a local directory) using the from_git
parameter.
When you create an ApiLogicProject, the system creates a project like this that you customize in your API:
You will observe that the projects are rather small. That is because the syste creates models that define what, not now. Explore the project and you will find it easy to understand the API, data model, app and logic files.
Note the entire project is file-based, which makes it easy to perform typical project functions such as source control, diff, merge, code reviews etc.
Your customizations are made to the files in the following sections. These are separate files from the core model and api files, so that (if you wish) you can recreate the system from a revised schema, then simply copy over the files described below.
Initially the API exposes all your tables as collection endpoints. You can add additional endpoints by editing expose_services.py
, as illustrated by the Add Service example. For more on customization, see SAFRS Customization docs.
Model files describe your database tables. Each database has extensions which can introduce issues in model generation, so facilities are described in Troubleshooting to edit models and rebuild.
Rebuild support enables you to rebuild your project, preserving customizations you have made to the api, logic and app. You can rebuild from the database, or from the model.
This enables you to edit the model to specify aspects not captured in creating the model from your schema. For example, sqlite often is not configured to enforce referential integrity. SQLAlchemy provides support to fill such gaps.
For example, try to delete the last order for the first customer. You will encounter an error since the default is to nullify foreign keys, which in this case is not allowed.
You can fix this by altering your models.py:
OrderDetailList = relationship('OrderDetail', cascade='all, delete', cascade_backrefs=True, backref='Order')
Your api, logic and ui are not (directly) dependent on this setting, so there is no need to rebuild; just restart the server, and the system will properly cascade the Order
delete to the OrderDetail
rows. Note further that logic will automatically adjust any data dependent on these deletions (e.g. adjust sums and counts).
In addition, you may wish to edit models_ext.py
, for example:
-
to define relationships, critical for multi-table logic, APIs, and web apps
-
to describe derived attributes, so that your API, logic and apps are not limited to the physical data model
As of release 5.02.03, created API Logic Projects integrate Alembic to perform database migrations.
- Manual: create migration scripts by hand, or
- Autogenerate: alter your
database/models.py
, and have alembic create the migration scripts for you
Preconfiguration includes:
- initialized
database/alembic
directory - configured
database/alembic/env.py
for autogenerations - configured
database/alembic.ini
for directory structure
See the readme
in your database/alembic
for more information.
admin.yaml
describes form layout. Customize it, for example to control column layouts, etc.
Ignoring the boxes labeled "rebuild", the key elements of the creation process are illustrated below:
-
the system reads the database schema to create
models.py
-
models.py
drives the creation process -
you customize the created project, mainly by altering the files on the far right
As shown in the diagram, creation is always driven from models.py.
Models differ from physical schemas in important ways:
-
the system ensure that class names are capitalized and singular
-
there are good reasons to customize
models.py
:- to add foreign keys missing in the database - these are critical for multi-table apis and applications
- to provide better naming
You can rebuild your project, preserving customizations (including any additional files you have created). You have 2 alternatives, depending on where you choose the "source of truth" for your database:
Source of Truth | Means | Use rebuild option |
---|---|---|
Database | The schema is the source of truth It includes all the foreign keys |
rebuild-from-datatabase: rebuilds the files shown in blue and purple. |
Model | Model is the source of truth Use SQLAlchemy services to drive changes into the database |
rebuild-from-model: rebuilds the files shown in blue |
Note that ui/admin/admin.yaml
is never overwritten (the dotted line
means it is written on only on create
commands). After rebuilds, merge the new ui/admin/admin-created.yaml
into your customized admin.yaml.
As of release 5.02.03, rebuild
services provide support for updating customized API and Admin:
System Object | Support |
---|---|
API |
api/expose_api_models_created.py created with new database/models.py classes |
Admn App |
ui/admin/admin-merge.yaml is the merge of ui/admin/admin.yaml and new database/models.py classes |
Review the altered files, edit (if required), and copy them over the original files.
Most of API Logic Server functionality derives from the data model classes created from your schema when you create your project. Here is an example:
Observe that:
-
A class is created for each table. The name (e.g.
OrderDetail
) is derived from the table name, but is capitalized and singlularized -
The table name is from your schema, this corresponds to a resource collection in the API
-
Relationships are created on the one side of one-to-many relationships. The relationship name is the target class + "List", and is available in Python (
items = anOrder.OrderDetailList
). These names are used in your UI admin apps, and your API -
Relationships have 2 names; the backref name is now the many side refers to the _one" side (e.g., anOrder = anOrderDetail.order`)
Relationship names are also part of your API:
The extended_builder
option enables you to extend the creation process. It is intended to accommodate cases where DBMSs provide proprietary features - such as Table Valued Functions (TVFs) - that should be exposed as APIs.
Install as usual, and create your project using the extended_builder
option, e.g:
ApiLogicServer run --db_url='mssql+pyodbc://sa:posey386!@localhost:1433/SampleDB?driver=ODBC+Driver+17+for+SQL+Server?trusted_connection=no' \
--extended_builder=extended_builder.py \
--project_name=TVF
Or, use the default extended_builder:
ApiLogicServer run --db_url='mssql+pyodbc://sa:posey386!@localhost:1433/SampleDB?driver=ODBC+Driver+17+for+SQL+Server?trusted_connection=no' \
--extended_builder='*' \
--project_name=TVF
to designate a file that implements your builder. After the creation process, the system will invoke extended_builder(db_url, project_directory)
so you can add / alter files as required.
Full automation for specific DBMS features was considered, but could not conceivably accommodate all the DBMS features that might be desired. We therefore provide this extensible automation approach.
Let's illustrate the use of extensible automation with this example. Create the sample project as follows:
- Acquire this sql/server docker database
- Create the project
docker run -it --name api_logic_server --rm -p 5656:5656 -p 5002:5002 -v ${PWD}:/localhost apilogicserver/api_logic_server
ApiLogicServer create --project_name=/localhost/sqlserver-types --db_url=mssql+pyodbc://sa:posey386!@localhost:1433/SampleDB?driver=ODBC+Driver+17+for+SQL+Server?trusted_connection=no
This uses an example extended builder can be found here. You can copy this file to a local directory, alter it as required, and specify its location in the CLI argument above. It is loosely based on this example.
The interface to ApiLogicServer requires that you provide an extended_builder(db_url, project_directory)
function, like this (the rest is up to you):
def extended_builder(db_url, project_directory):
""" called by ApiLogicServer CLI -- scan db_url schema for TVFs, create api/tvf.py
for each TVF:
class t_<TVF_Name> -- the model
class <TVF_Name> -- the service
args
db_url - use this to open the target database, e.g. for meta data
project_directory - the created project... create / alter files here
"""
print(f'extended_builder.extended_builder("{db_url}", "{project_directory}"')
tvf_builder = TvfBuilder(db_url, project_directory)
tvf_builder.run()
This particular example creates this tvf file in the api folder.
Updates api/customize_api.py
to expose it, as shown below:
This example illustrates the extended builder approach; the resultant services runs as shown below.
It does not deal with many data types.
It generates Swagger, with arguments:
You can run it with this cURL:
curl -X POST "http://localhost:5656/udfEmployeeInLocation/udfEmployeeInLocation" -H "accept: application/vnd.api+json" -H "Content-Type: application/json" -d "{ \"location\": \"Sweden\"}"
returns the expected data:
{
"result": [
2,
"Nikita",
"Sweden"
]
}
This section applies only to pip
installs. Docker based installs eliminate such environment issues, and are therefore recommended.
You created a virtual environment when you installed ApiLogicServer. This venv
will work for all of your created ApiLogicServer projects, or you can use a per-project venv
as described below.
Alternatively, you can create a self-contained virtual environment for each project.
The created project contains a requirements.txt
used to create a virtual environment.
You can create it in the usual manner:
cd ApiLogicProject
python3 -m venv venv # may require python -m venv venv
source venv/bin/activate # windows venv\Scripts\activate
pip install -r requirements.txt
ApiLogicServer creates a standard Flask-based 3-tier architecture:
-
Flask enables you to write custom web apps, and custom api end points
- ApiLogicServer automatically creates an Admin App using safrs-react-admin, useful for back-office admin access and prototyping
-
SAFRS provides the API, which you can use to support mobile apps and internal / external integration
-
SQLAlchemy provides data access.
-
Logic Bank listens for updates, and applies your declared logic, for both API and web app updates.
ApiLogicServer> cd my_new_project
my_new_project> python api_logic_server_run.py
This should now run http://localhost:5656/, and return data.
ApiLogicServer attempts to avoid port conflicts. These can arise from:
-
Common use of 8080
-
Mac use of 5000
To avoid conflicts, ports are defaulted as follows:
For | Port |
---|---|
ApiLogicServer | 5656 |
Basic Web App | 5002 |
Hosts are defaulted as follows:
Installed as | Basic Web App Host |
---|---|
Docker | 0.0.0.0 |
Local Install | localhost |
When you run created applications, you can provide arguments to override these defaults. For example:
ApiLogicServer run --project_name=~/dev/servers/api_logic_server --host=myhost --port=myport
python ~/dev/servers/api_logic_server/api_logic_server_run.py myhost myport # equivalent to above
Start the ApiLogicServer, and run your browser at
http://localhost:5656/
You can run the Basic Web App like this:
ApiLogicServer run-ui [--host=myhost --port=myport]
my_new_project> python ui/basic_web_app/run.py [host port]