Instant API Backends
One command to create an API for your database. And a Basic Web App. With spreadsheet-like business logic — 40X more concise. Extensible with Python.
Join the DZone community and get the full member experience.
Join For FreeIs backend creation blocking your Mobile App Dev? Or strategic B2B/internal integration?
Imagine that you could...
Create a database API, instantly?
And declare logic, with spreadsheet-like rules?
Plus an instant Web App, to engage Business Users?
Well, you've just imagined API Logic Server! Working software... now!
In this 10-20 minute tutorial, we'll create, explore and customize the 3 main elements of an API Logic Server:
- A JSON:API for a database
- Business Logic
- A Basic Web App
ApiLogicServer is Open Source, available on GitHub, with over 18k downloads in a little over 2 months. Demo video here.
Create ApiLogicServer
In this step, we'll use the ApiLogicServer CLI to create and run an api_logic_server project (an API and Web App, backed by underlying Logic):
To create the ApiLogicServer:
- Paste the following into a Terminal window:
xxxxxxxxxx
ApiLogicServer run
Accept the default
Database URL
parameter to use the supplied sample database.- After this Tutorial, you can try it with your own database, by specifying a SQLAlchemy URL (see links below).
Quite a Lot Just Happened
Quite a lot just happened that substantially accelerates your project. The process is shown in the diagram below, mapping your actions (A, B, C -- you just did step B) onto a classic n-tiered modern software architecture:
What You do | What System Does | Why It Matters |
---|---|---|
A) Create DB | (Presupplied for demo) | Use existing tools and procedures |
B) ApiLogicServer run | 1) Instant API: From schema, creates an endpoint for each table - CRUD Data Access automation, including filtering. pagination and related data access | Custom UI development is unblocked on API coding (routes, controllers, ORMs...) |
2) Instant Web App: Multi-page, multi-table | Engage business users with working software, early in the project | |
C) Declare Logic | Executes spreadsheet-like rules that automate the cocktail napkin spec | Business Agility: 40X more concise, customizable with Python |
1. Explore the API
In many projects, User Interface development is blocked by waiting on API creation.
The run
command not only created the project, but it ran python api_logic_server/api_logic_server_run.py
to start the server. Let's explore it.
Key Takeaway: ApiLogicServer unblocks UI development
with instant API creation.
Open API (Swagger)
Your API can be explored with Open API (Swagger). Explore the api at https://localhost:5000, and:
- Click Customer
- Click Get
- Click Try it out
- Click Execute
Swagger is a great way to discover the API, and get copy/paste code for UI app dev.
Get: Pagination, Filtering, Configurable Fields, and Joins
In addition to Open API, we can use curl.
- Paste this into a terminal window:
xxxxxxxxxx
curl -X GET "http://localhost:5000/Order\
?page%5Boffset%5D=0\
&page%5Blimit%5D=2\
&sort=Id%2CCustomerId%2CEmployeeId\
&filter%5BCustomerId%5D=ALFKI"\
-H "accept: application/vnd.api+json" \
-H "Content-Type: application/vnd.api+json"
- Observe the response in the Terminal window
Pagination, Filtering
In the GET
request, observe:
page offset
- pagination supportfilter
- autmatic support for filtering rows
Configurable Fields, Joins
JSON:APIs - automated by safrs - are client configurable. This is valuable because:
- Client Configurable APIs can reduce network traffic - a single call instead of multiple calls on predefined APIs that don't return quite the correct data
- Client Configurable APIs can reduce organizational dependencies - instead of a waiting on creation of a wide number of endpoints from the server team, UI developers can configure their own APIs
In the example below, observe the following in the GET
request:
fields
to designate the fields to be returnedinclude
to designate related data
- Paste this into a Terminal window:
xxxxxxxxxx
curl -X GET "http://localhost:5000/Order\
?include=OrderDetailList\
&fields%5BOrder%5D=Id%2CCustomerId%2CEmployeeId%2COrderDate%2CAmountTotal\
&page%5Boffset%5D=0\
&page%5Blimit%5D=2\
&sort=Id%2CCustomerId%2CEmployeeId\
&filter%5BCustomerId%5D=ALFKI"\
-H "accept: application/vnd.api+json" \
-H "Content-Type: application/vnd.api+json"
- In the response, observe the
"included": [
tag - the list of relatedOrderDetail
records:
x
"included": [
{
"attributes": {
"Amount": "684.0000000000",
"Discount": 0.25,
"Id": 1040,
"OrderId": 10643,
"ProductId": 28,
"Quantity": 15,
"ShippedDate": "",
"UnitPrice": "45.6000000000"
},
"id": "1040",
"links": {
"self": "http://localhost:5000/OrderDetail/1040/"
},
Patch: Logic Enabled Updates
Your API also includes services for insert, update and delete. We can use curl to test the update.
- Paste the following into a Terminal window:
xxxxxxxxxx
curl -vX PATCH "http://localhost:5000/Customer/ALFKI/" -H "accept: application/vnd.api+json" -H "Content-Type: application/json" -d '
{
"data": {
"attributes": {
"CreditLimit": "100"
},
"type": "Customer",
"id": "ALFKI"
}}'
- Observe the update request reports the violation from our predefined constraint rule:
xxxxxxxxxx
balance (1016.0000000000) exceeds credit (100)
This was entirely intentional, to illustrate the transaction logic underlying the API. Let's explore that.
2. Explore Transaction Logic
The classic definition of Business Logic covers 2 aspects:
- Data Access — this was automated in the
ApiLogicServer run
step, which created not only the API, but the data access (based on SQLAlchemy) - Transaction logic — multi-table derivations, constraints, and actions such as sending mail or messages — is a significant aspect of any database oriented system, nearly half
Logic is the iceberg under the surface of the API.
ApiLogicServer provides a unique
declarative, rules-based approach
for automating transaction logic.
Cocktail Napkin Explosion
It's striking how a small "cocktail napkin specification" balloons into hundreds of lines of code:
Implementing logic by conventional procedural code is
slow, error prone, and painful to maintain.
Logic Is Declarative
Api Logic Server dramatically improves conciseness, quality and maintainability, by introducing a significant innovation for automating transaction logic: Logic Bank. Logic consists of:
- Rules — 40X more concise using a declarative, spreadsheet-like paradigm, and
- Python — control and extensibility, using standard tools and techniques
Note: in this tutorial, the API and Web App were created strictly from the data model. The logic, however, is injected so you can explore it.
Rules are not simply procedural event handlers. They are spreadsheet-like expressions for constraints and derivations, applied on commit. The process is follows:
- Design as you normally would, and specify the behavior - the "cocktail napkin spec" above
- Declare rules to express your design
- You will find that there is a very close mapping from your design to the rule declarations, as shown below
- These 5 rules replace all the Python code shown above — 200 lines
- Extend with Python (shown later):
This table summarizes the key declarative/procedural differences:
Characteristic | Procedural | Declarative | Why It Matters |
---|---|---|---|
Reuse | Not Automatic | Automatic - all Use Cases | 40X Code Reduction |
Invocation | Passive - only if called | Active - call not required | Quality |
Ordering | Manual | Automatic | Agile Maintenance |
Optimizations | Manual | Automatic | Agile Design |
Unlike code, you do not call the rules directly. The rules engine in Logic Bank listens for SQLAlchemy before_flush
events, and applies the applicable rules in an order that reflects their dependencies, with chaining.
For more information on rules, see the links at the end.
The constraint rule (line 54, above) is what caused the update error response above - the Balance
exceeded the altered CreditLimit
. The transaction is rolled back.
Perspective: 40X
Let's pause for some perspective: what is 40X more concise?
A jetliner flies 4X the speed of Charles Lindbergh's Spirit of St. Louis. To get to 40X, you need a scram jet.
So, rules mean that for nearly half your system, you are writing one rule instead of nearly a page of code. See the contrast in the links, at the end, for the actual code vs. rules contrast.
Explore Types, Examples
The links (at the end) include a complete list of rules, and examples.
Key Takeaway: Logic is 40X more concise, higher quality, and easier to maintain than legacy procedural code.
3. Explore the Basic Web App
UI development takes time. That's a problem since:
- Such effort may not be warranted for admin "back office" screens, and
- Agile approaches depend on getting working software soon, to drive collaboration and iteration.
ApiLogicServer CLI therefore creates working software now: multi-page, multi-table applications as shown below:
- Multi-page: apps include 1 page per table
- Multi-table: pages include
related_views
for each related child table, and join in parent data - Favorite fields first: first-displayed field is "name", or
contains
"name" (configurable) - Predictive joins: favorite field of each parent is shown (product name - not product id)
- IDs last: such boring fields are not shown on lists, and at the end on other pages
Start the application:
xxxxxxxxxx
cd ApiLogicServer/api_logic_server
python ui/basic_web_app/run.py
Before running, you must Create Admin Data for Flask App Builder (except for the Northwind sample, which is pre-created).
Customize With Python
Recall using API Logic Server CLI:
xxxxxxxxxx
ApiLogicServer run
created a complete, executable ApiLogicServer project in a directory called api_logic_server
. On your own machine, you could open the project in an IDE (PyCharm, VSCode, etc) or an Editor (Atom, Text Wrangler, etc). It looks like this in PyCharm:
ApiLogicServer CLI introspected your database and created a database/models.py
file (for SQLAlchemy), and files that declare your api
and ui/basic-web-app
. You can edit those files to customize your server, as described below.
Customize the Data Model
You may need to extend the initially created data model classes, to define derived attributes, or to add relationships not defined in the schema. Such extensions are added to a separate file, to avoid merges if you ever need to rebuild the model classes from a changed schema.
Add code like this to database/models_ext.py
:
xxxxxxxxxx
models.Employee.Manager = relationship('Employee', cascade_backrefs=True,
backref='Manages',
primaryjoin=remote(models.Employee.Id) == foreign(models.Employee.ReportsTo))
# add derived attribute: https://github.com/thomaxxl/safrs/blob/master/examples/demo_pythonanywhere_com.py
def proper_salary(row):
if not hasattr(row, "_proper_salary"):
row._proper_salary = row.Salary * 1.25 # create the attr with default value
return row._proper_salary
setter .
def proper_salary(row, value):
row._proper_salary = value
# print(f'_proper_salary={row._proper_salary}')
pass
models.Employee.ProperSalary = proper_salary
print("models_ext.py successfully executes: models.Employee.Manager = relationship('Employee', cascade_backrefs=True, backref='Manages'")
Customize the API
Your API is derived from the database, but it is not restricted to that. You can customize your API, e.g., add new endpoints and services.
Trivial APIs: Hello World
For example - an instant API for a database is all well and good - but can we build the "hello world" so desperately sought by enterprises all over the world? We can.
Examine the following code in api_logic_server/api/expose_services.py
:
xxxxxxxxxx
route('/hello_world') .
def hello_world(): # test it with: http://localhost:5000/hello_world?user=ApiLogicServer
"""
This is inserted to illustrate that APIs not limited to database objects, but are extensible.
See also: https://github.com/thomaxxl/safrs/wiki/Customization
"""
user = request.args.get('user')
return jsonify({"result": f'hello, {user}',
"notice": f'add your own endpoints with python'})
You can run this with curl:
xxxxxxxxxx
curl -X GET "http://localhost:5000/hello_world?user=ApiLogicServer"
Services: Add Order
You can also create multi-table services as shown below, for adding an Order
and a list of OrderDetails
.
Key observations:
Service Definition: to define a service:
a. Edit
expose_services.py
, and define a decorated method (line 94). The payload (Order
and a list ofOrderDetails
) is passed in as*kwargs
.b. Your first comment (line 98) describes the arguments in YAML, along with sample data, exposed in Swagger.
Service Processing
a. Use standard Python (lines 110-116) for your service code, including SQLAlchemy and other Python packages.
Note the use ofjson_to_entities
, to transform the json payload into mapped SQLAlchemy model objects. It is generic, so can be used with your own model objects.b. It's short, since transaction logic is factored out and automatically reused. The rules that fired for changing a Customers' CreditLimit also monitor order processing. If the json argument contains an absurdly high
quantity
, the logic detects this and throws an error. This is shown in the log in the bottom pane, where the indentation illustrates the chaining of a multi-table transaction.
On the last few lines of the log:
1. OrderDetail.Amount is derived
2. OrderDetail.Amount adjusts the Order.TotalAmount
3. the Order.TotalAmount adjusts the Customer.Balance
4. which is checked in the constraint
Key Takeaway: Extend API with Python
Your API is not restricted to the database model -
extend it with standard Python
Key Takeaway: Automatic Reuse
In traditional procedural coding, re-use is generally achieved with significant design work. But in this declarative approach, logic is automatically re-used for all transactions. That has a significant impact on conciseness, and quality.
Customize Logic
Use Python to customize logic. Let's POST a new Order:
- Paste the following into the a Terminal window:
xxxxxxxxxx
curl -X POST "http://localhost:5000/Order/"\
-H "accept: application/vnd.api+json" -H "Content-Type: application/json"\
-d "\
{ \"data\": {\
curl -X POST "http://localhost:5000/Order/"\
-H "accept: application/vnd.api+json" -H "Content-Type: application/json"\
-d "\
{ \"data\": {\
\"attributes\": {\
\"CustomerId\": \"ALFKI\",\
\"EmployeeId\": 1,\
\"Freight\": 10},\
\"type\": \"Order\"\
}\
}"
- Observe the line containing Congratulations in the log (bottom pane), generated by the event declared on line 64, and implemented on line 44:
This illustrates that you can leverage the full power of Python for events, and more complex constraints and formulas, to complement the expressive power of rules.
Key Takeaway: ApiLogicServer spreadsheet-like logic is extensible with standard Python.
Customize the Web App
The Basic Web App is driven by ui/basic_web_app/app/views.py
, which contains classes like this for each table:
class CustomerModelView(ModelView):
datamodel = SQLAInterface(Customer)
list_columns = ["CompanyName", "ContactName", "ContactTitle", "Address", "City"]
show_columns = ["CompanyName", "ContactName", "ContactTitle", "Address", "City", "Region", "PostalCode", "Country", "Phone", "Fax", "Balance", "CreditLimit", "OrderCount", "Id", "UnpaidOrderCount"]
edit_columns = ["CompanyName", "ContactName", "ContactTitle", "Address", "City", "Region", "PostalCode", "Country", "Phone", "Fax", "Balance", "CreditLimit", "OrderCount", "Id", "UnpaidOrderCount"]
add_columns = ["CompanyName", "ContactName", "ContactTitle", "Address", "City", "Region", "PostalCode", "Country", "Phone", "Fax", "Balance", "CreditLimit", "OrderCount", "Id", "UnpaidOrderCount"]
related_views = [OrderModelView]
appbuilder.add_view(
CustomerModelView, "Customer List", icon="fa-folder-open-o", category="Menu")
You can edit this file to control what columns are displayed, their display order, and what related data (views) are shown.
Flask AppBuilder has a wide range of capabilities, including charts. For more information, see Flask AppBuilder.
Key Takeaway: ApiLogicServer multi-page, multi-table apps provide instant back-office admin and agile prototyping.
ApiLogicServer Tutorial — Wrap-up
In 20 minutes — instead of weeks or months — you have built and executed an ApiLogicServer, and explored its 3 key elements:
Element | Key Takeaway | Why It Matters |
---|---|---|
JSON:API | Created instantly, customizable | Unblock UI Development |
Logic | Spreadsheet-like rules automate the cocktail napkin spec 40X more concise, customizable with Python |
Strategic Business Agility |
Basic Web App | Multi-page, multi-table - created instantly, customizable | Engage Business Users, early |
That's a significant result.
This combination of an instant API and spreadsheet-like logic - customizable with Python - may mean it's time to take a very close look at this technology, to create database backends with strategic business agility.
References
Installation
Local Install: how to install Python and ApiLogicServer locally. ApiLogicServer installs with a Northwind sample database, used in this Tutorial.
Cloud-based demo: if you are considering Jupyter notebooks for your own no-install demos, you can explore how this demo was built on GitHub.
Create ApiLogicServer
- Here is how to specify a SQLAlchemy url
- Here is more information on safrs, the underlying API engine
Logic - Explore Types, Examples
Here is a complete list of rule types.
To see rules in action, explore the Examples.
For a general background on logic, see the Logic Bank Overview
Logic - Perspective: 40X
- See the logic vs code contrast here
Explore Basic Web App
- Before running, you must Create Admin Data for Flask App Builder (except for Northwind, which is pre-created)
Customize Logic
- You can leverage the full power of Python for events, and more complex constraints and formulas
Opinions expressed by DZone contributors are their own.
Comments