Generating OpenAPI clients for FastAPI via Gradle
Tags: fastapi, openapi, gradle, angular
Published: 2020-06-06 12:02:32

---
title: Generating OpenAPI clients for FastAPI via Gradle
published: true
description: 
tags: fastapi, openapi, gradle, angular
---

[FastAPI](https://fastapi.tiangolo.com/) is a neat python server framework that allows us to setup a server quickly . It automatically generates an OpenAPI spec via its decorated methods and exposes it when the server is running.

Sometimes we want to generate some kind of client directly e.g. for an Angular application in the same repo or for some other client maybe for some E2E Tests without launching the server.

This post demonstrates how we can generate a client for Angular via Gradle and the OpenAPITools generators.

This approach can be used to generate any kind of client that is supported by OpenAPITools.

The full working demo can be found here:

https://github.com/mxab/fastapi-angular-openapi-gradle-demo

Let's assume our repo structure looks like this:

``` 
.
├── fastapi-server
│   ├── build.gradle
│   ├── fastapi_server
..
│   ├── poetry.lock
│   ├── pyproject.toml
└── ng-app
│   ├── build.gradle
│   ├── angular.json
..
    └── src
        ├── app
```

## Exporting the OpenAPI Spec
The `fastapi-server` directory contains the server code and organises its dependencies via poetry

To now export an OpenAPI JSON file we create a Gradle task inside this project that looks like this:

Inside a FastAPI application you can programmatically access the OpenAPI Spec as a dict via `app.openapi()`. Let's leverage this function to generate the json spec:

```groovy
ext {
// the location of the exported file
    openapiJSONSpec = file("$buildDir/openapi/fastapi-server.spec.json")
}

tasks.register("exportOpenAPISpec", Exec) {
    commandLine = [
        "poetry",
        "run",
        "python", 
        "-c",
        """
        import json
        import sys
        from fastapi_server.app import app
        json.dump(app.openapi(),sys.stdout)
        """.stripIndent()]
    
    openapiJSONSpec.parentFile.mkdirs()//otherwise FileOutputStream throws
    standardOutput = new FileOutputStream(openapiJSONSpec)

    inputs.dir file("fastapi_server")
    outputs.file openapiJSONSpec
}

```
This `Exec` Tasks launches a python command via poetry so we automatically use the correct venv.

It imports the FastAPI `app` retrieves the openapi dict and dumps it as json to the `stdout`.

The Gradle task prints the output of the process to a file.

You can run `./gradlew fastapi-server:exportOpenAPISpec` to see the result.

But we are not completely done inside this project. That we can consume this generate json artifact from other projects inside our multi project we need to make Gradle aware of it.

Therefore we create a new configuration called `openapi` that can be consumed.

```groovy
configurations {
    openapi {
        canBeConsumed = true
        canBeResolved = false
    }
}
```

And we also tell Gradle that the generated file is an artifact of this configuration that is build by the task we registered:

```groovy
artifacts {
    openapi(openapiJSONSpec){
        builtBy(exportOpenAPISpec)
    }
}
```
Check the [Gradle docs](https://docs.gradle.org/current/userguide/cross_project_publications.html) for more details on this.

## Generate the angular client

In your angular project we want to use the `OpenAPITools` generator to generate a client.
### OpenAPITools Gradle plugin
We can use the official Gradle plugin for that.
```groovy
plugins {
    id "org.openapi.generator" version "4.3.0"
}
```

### Retrieving the spec file as dependency
In this project we also define a configuration that is resolvable and add the `fastapi-server` consumable config as dependency.

```groovy
configurations {
  openapi {
    canBeConsumed = false
    canBeResolved = true
  }
}
dependencies {
  openapi(project(path: ":fastapi-server", configuration: 'openapi'))
}

```

### Dependency as input for the generator task

We can now simply tell the `openApiGenerate` task that it should use the resolved file from the openapi configuration.

```groovy
ext {
    generator = "typescript-angular"
    clientOutputDir = file("$projectDir/src/client")
}
openApiGenerate {
    generatorName = generator
    outputDir = clientOutputDir.path
    inputSpec = configurations.openapi.singleFile.path
}

//unfortunatly the actual task does not know anything about inputs and outputs. This tweak fixes it
tasks.named("openApiGenerate"){
    inputs.files configurations.openapi
    outputs.dir clientOutputDir
}

```

When we now run
```bash
./gradlew ng-app:openApiGenerate
```

it first runs the export task and then the generate task. When we look into `ng-app/src/client` we will see a ready made client for our backend to use.

### Use in angular

In our main `app.module.ts` we can now import it as a module:

```typescript
import { ApiModule, BASE_PATH } from '../client';
...
import { environment } from './../environments/environment';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
   ...
    ApiModule
  ],
  providers: [{
    provide: BASE_PATH, useValue: environment.apiUrl
  }],
  bootstrap: [AppComponent]
})
export class AppModule { }
```

And finally we inject the `DefaultService` where it's needed:

```typescript
@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
 constructor(private readonly defaultService: DefaultService){
 ...
  this.defaultService.listUsersUsersGet()
 ...
 }
}

```

## Conclusion

Gradle allows us to enhance our project setup easily, especially when there are multiple heterogeneous projects.

The export part could be adjusted to whatever python tooling that is used (conda, pipenv...)

The the OpenAPITools project provides many different [generators](https://openapi-generator.tech/docs/generators) which allows us to adjust this show case for different use cases.