The service layer is conceptually similar to a web framework in that there are apps, endpoints, middleware, requests and responses. As a compared to a web framework, however, Ravel has a more abstract notion of what these things are. Ultimately, the service layer presents a public interface to the outside world.
As mentioned before, the architecture of the service layer is a lot like a web framework, but there are some important additions--namely, stages 2, 3, and 7 below.
In the first stage, middleware has a chance to initiate services and access the arguments with which an API endpoint was called. Note that arguments may differ, depending on the type of Ravel application being run. In a web app, these arguments might consist of an HTTP request; for a gRPC service, they'd consist of protocol buffer messages.
In stage two, Ravel inspects the arguments with which an endpoint was called. It then constructs the positional and keyword arguments expected by the function targeted by the endpoint.
Imagine a signup
function that we'd like to reuse as an endpoint in a Ravel WSGI app. The Application
instance in this example is called web
. Under the hood, a web framework is running that provides only HTTP request as arguments. In contrast, our imaginary function expects something entirely different: namely, email and password.
@web(method='POST')
def signup(email: str, password: str) -> User:
...
The web framework does something like this:
web.api.signup(request)
As you may have guessed, values for email
and password
must be extracted from the raw HTTP request.
Once extraction is complete, the next stage it to replace arguments which refer to application Resource
objects (like users, accounts, etc.) by ID with the objects themselves. This process is based on the Python type annotations provided for endpoint arguments and return values.
Suppose we have a login
endpoint with a user
argument, annotated as a Ravel User
resource.
@app(method='POST')
def login(user: User, password: str) -> User:
...
After resolving the user
argument, all of the following API calls are equivalent:
app.api.login(user, password)
app.api.login('USER-123', password)
app.api.login({'_id': 'USER-123'}, password)
At this point middleware can hook into the resolved arguments ready to be passed into the endpoint function.
With the prepared arguments in hand, we finally call the endpoint function. If you are using a web framework through Ravel, note that all of its middleware runs within this stage.
In post-request, we can perform and cleanup or finalization of middleware services initiated in pre-request or on-request. In addition, we can inspect and alter the raw value returned by the endpoint function.
The last stage involves generating a final output value. In web apps, for instance, the final value is an HTTP response object.
Ravel apps are configured, initialized, and run by instances of the Application
class. These objects provide centralized access to endpoints, data stores, and resource types. In addition, they expose application endpoints through an API.
In addition to the Application
base class, Ravel comes with several prepackaged extensions.
- Interactive Shell (REPL) - Useful for debugging.
- Command-line Application (CLI) - Useful for creating bin scripts.
- Standard Library JSON HTTP Server - Based on Python's built-in
HttpServer
. - gRPC Service - gRPC service with automated build process.
- Falcon Web Service - High-performance production web service.
- Development WebSocket Service - Built on thirty-party
websockets
library.
Endpoints make up the public API of Ravel apps and are registered via decorator, like so:
app = Application()
@app()
def my_endpoint(arg1, arg2):
...
After bootstrapping an application, endpoints are accessible through an api
property.
app.api.my_endpoint('arg1', 'arg2')
Middleware provides additional services at key points while processing requests. Like traditional middleware in web frameworks, Ravel middleware can do things like manage database transactions and trace requests. In contrast, Ravel middleware can be reused in radically different computing contexts. For example, the same middleware for database transaction management can be used while calling an API method in a REPL as though HTTP.
Ravel Request
objects hold references the arguments supplied to endpoints. They accumulate additional state of various kinds during endpoint execution.
raw_args
,raw_kwargs
- Positional and keyword arguments with which the endpoint was called.prepared_args
,prepared_kwargs
- Argument values generated by the Argument Resolution execution stage.
Ravel Response
objects hold references to both the raw values returned by endpoints and the result of processing said value in order to prepare and marshal it out.
raw_result
- The raw value returned by the endpoint function.prepared_result
- The processed value, ready to be marshaled out by the hostApplication
.