Enhance the quality of your API by integrating Zally in GitLab CI

José Collado, Software Engineer at EDICOM, explains how to increase the quality of your API by integrating Zally into GitLab. Discover what Zally is, how to integrate it and some of the rules it has provided in our projects.

    Written by:

    José Collado

    Full Stack Developer

    Full Stack Developer specialized in Angular and Spring Boot, with an interest in User Experience and Usability.


Today, software development companies are placing increasing importance on the quality of their products, as well as on the automation of tools to ensure this quality.

A study carried out by World Quality Report including 1200 businesses concluded that 61% of them focus on the quality of their software solutions as a key objective. In addition, 51% have implemented Continuous Integration processes to detect whether quality requirements are met when introducing new code.

At EDICOM we believe that quality is an essential element when developing our software, so we decided to go for tools that help us detect and correct errors in our products, as we have already seen with Google Lighthouse.

What is Zally?

Zally is a linter created by Zalando which, based on an OpenAPI specification, checks if the API complies with the rules included in its good practice guide.

With Zally, we enhance the quality of our API by detecting errors and bad practices in the early stages of development. It will also help us to standardise our APIs by giving them a similar look and feel.

Enhance the quality of your API by integrating Zally in GitLab CI

We can use it through its RESTful API, from the CLI or by deploying its web interface.

A quick start guide to deploy it locally and test it can be found in its GitHub repository.

Zally integration in GitLab CI

1. Application deployment with Docker

As we can read in its documentation, Zally is an application whose server is developed with Spring Boot and the web with React. To follow this article, we will need at least to deploy the server side. There are different ways to do this, although the simplest is to deploy it with Docker using the docker-compose file they provide, which will set up the backend on port 8000 and the web interface on port 3000.

2. Automatic generation of OpenAPI specification

The application into which we want to integrate Zally consists of a backend developed in Spring Boot, so we will use the Maven plugin springdoc-openapi-maven-plugin to auto-generate the OpenAPI specification in each pipeline of a Merge Request.

To do so, we must have previously set up the springdoc-openpi plugin in the project.

This is the plugin configuration that we will include in the pom.xml, where apiDocsUrl will be the local URL where we are generating the OpenAPI specification with springdoc-openapi.


This is the plugin configuration that we will include in the pom.xml , where apiDocsUrl  will be the local URL where we are generating the OpenAPI specification with springdoc-openapi

If we have set up the plug-in correctly, launching the  mvn integration-test command will generate an openapi.json file in the project’s target directory.

    "openapi": "3.0.1",
    "info": {
        "title": "Backend Articulo Zally",
        "description": "Descripcion del api REST",
        "version": "v0.1.0-3b6167f"
    "servers": [
            "url": "http://localhost:8080/api/v1",
            "description": "URL API localhost"

3. Create stage for Zally

Once we have the OpenAPI specification generated in the Merge Request pipeline, we can create a stage in GitLab CI that calls the Zally server and returns the validation. For this, we have created a bash script that launches a POST request with the file openapi.json in the request body:

 LINT_RESPONSE=$(curl -s -X \
        POST $SERVER_URL/api-violations -o output.json \
        --header 'Content-Type: application/json' \
        --data "@body.txt")

Where body.txt contains a JSON with the following structure:

    "api_definition": {},
    "api_definition_string": "",
    "api_definition_url": "",
    "ignore_rules": []

These are the arguments that the script receives and which will be passed on to it as IC variables:

  • $SPEC_LINT_FILE: File with the OpenAPI specification (in JSON or YAML format)
  • $SPEC_LINT_URL: If our specification is not in a file but in a URL.
  • $ZALLY_SKIN_URL: URL where the Zally web interface is located.
  • $ZALLY_SERVER_URL: Zally Server URL.
  • $IGNORED_ZALLY_RULES: Ids of the Zally rules we want to disable, separated by commas.
  • $MUST_THRESHOLD: Threshold above which we want the stage result to be a failure.

And so we will call the function we have created in the bash script:

source ./zally-curl-validation.sh

4. Validation result

The response we received to the above request contains the validation and has the following structure:

    "external_id": "aece171b-42ae-4c3d-8b4e-cef0d0dac9fa",
    "message": "",
    "violations": [
            "title": "Lowercase words with hyphens",
            "description": "Use lowercase separate words with hyphens for path segments",
            "violation_type": "MUST",
            "rule_link": "https://zalando.github.io/restful-api-guidelines/#129",
            "paths": [
            "pointer": "/paths/~1pet~1findByTags",
            "start_line": null,
            "end_line": null
    "violations_count": {
        "must": 89,
        "should": 25,
        "may": 1,
        "hint": 0

Next we will indicate whether the stage should fail or not, depending on the number of MUST rules and the  $MUST_THRESHOLD variable:

    MUST=$(jq -r '.violations_count.must' <<<"$RESPONSE")
    if [ "$MUST" -gt "$FAIL_THRESHOLD" ]; then
        exit 1
        exit 0

To view the report from the skin, we concatenate the URL of the web with the validation id obtained in the response:

echo -e "$SKIN_URL/editor/$EXTERNAL_ID"

The final result of the execution will be as follows:

Enhance the quality of your API by integrating Zally in GitLab CI

Report and conclusions

Finally, we will analyse some of the rules that Zally has brought us in our projects.

  • Use snake_case for property names, query params, etc. (Rule 118)
deleteFile -> delete_file
/deployment -> /deployments
  • Date type properties must use the suffix “at”. (Rule 235)
startTime -> started_at
  • The response to the calls must be JSON objects. (Rule 110)
Respuesta con un entero: 215 -> { "total_deployments": "215" }
  • API version must comply with the Semver format. (Rule 116)

As we can see, these are rules that correspond to good programming practices and help create standards that facilitate the consumption of APIs by other developers.