OpenAPI support in Galaxy and Interactive API Documentation
Since Galaxy release 22.05 it's much easier to discover, explore, learn and experiment with the Galaxy Rest API.
Thanks to the recent development efforts in modernizing the Galaxy backend by migrating our custom API framework to FastAPI and moving from a Synchronous (WSGI) to an Asynchronous Server Gateway Interface (ASGI), the Galaxy API is now OpenAPI compliant. This enables many new features that were not possible with the previous framework.
One of those new features is that we now have interactive API documentation using Swagger!
You can go to https://usegalaxy.eu/api/docs and try it out now!
Let's have a look at how we are migrating our existing API routes
As an example, we can see how the show quota details route was migrated in the Quotas API.
Usually, the first step is moving the existing API logic to a service layer. Then, in the API module, we can focus on documenting each endpoint and keep the logic in a different class QuotasService that has extensive Python type hints and returns Pydantic models. These Pydantic models annotate each field with additional information used by FastAPI to generate the interactive documentation.
Here's an example of the QuotaDetails Pydantic model. It provides type hints for each field, default values, title and description:
class QuotaDetails(QuotaBase):
description: str = QuotaDescriptionField
bytes: int = Field(
...,
title="Bytes",
description="The amount, expressed in bytes, of this Quota.",
)
operation: QuotaOperation = QuotaOperationField
display_amount: str = Field(
...,
title="Display Amount",
description="Human-readable representation of the `amount` field.",
)
default: List[DefaultQuota] = Field(
[],
title="Default",
description="A list indicating which types of default user quotas, if any, are associated with this quota.",
)
users: List[UserQuota] = Field(
[],
title="Users",
description="A list of specific users associated with this quota.",
)
groups: List[GroupQuota] = Field(
[],
title="Groups",
description="A list of specific groups of users associated with this quota.",
)
Then, the api/quotas/{id}
route in our Quotas API module will be something like this:
@router.cbv
class FastAPIQuota:
service: QuotasService = depends(QuotasService)
...
@router.get(
"/api/quotas/{id}",
summary="Displays details on a particular active quota.",
require_admin=True,
)
def show(
self, trans: ProvidesUserContext = DependsOnTrans, id: EncodedDatabaseIdField = QuotaIdPathParam
) -> QuotaDetails:
"""Displays details on a particular active quota."""
return self.service.show(trans, id)
While the show
function in the QuotasService class is the same that we had in the previous API framework, now, it uses Pydantic models and type annotations to document the parameters and return models.
class QuotasService(ServiceBase):
"""Interface/service object shared by controllers for interacting with quotas."""
...
def show(self, trans: ProvidesUserContext, id: EncodedDatabaseIdField, deleted: bool = False) -> QuotaDetails:
"""Displays information about a quota."""
quota = self.quota_manager.get_quota(trans, id, deleted=deleted)
rval = quota.to_dict(view="element", value_mapper={"id": trans.security.encode_id, "total_disk_usage": float})
return QuotaDetails.parse_obj(rval)
Galaxy is a complex application and its API is considerable large! So there are still some undocumented routes out there. If you are familiar with Galaxy and want to help migrate more API routes or improve the existing documented ones, you are very welcome! Please look at this GitHub issue to track which APIs still need documentation.