Part of our attachmentAV offering is an API, powered by Amazon API Gateway (REST APIs), allowing developers to integrate virus and malware scanning into their applications. To increase discoverability and simplify integration we decided to build software develpment kits (SDKs) for popular programming languages.
Learn from my journey of finding the best way to generate SDKs for JavaScript, Java, Python, and TypeScript for a REST API.
Generating the SDK was simple and fast. However, the result was mediocre. From a developer point of view, using the SDK did not provide significant convience compared to sending plain HTTP request. I was missing an layer of abstraction to simplifiy API usage.
Smithy: Interface Definition Language (IDL)
Second, I looked into Smithy, an Interface Defintion Language developed by AWS to generate their SDKs. Who, if not AWS, must know how to generate SDKs for APIs.
The concept of an extensible, typesafe, protocol agnostic IDL in combination with tools to build client SDKs for various programming languages resonated with me. I found the layer of abstraction, that I was missing while generating SDKs with Amazon API Gateway.
The following snippet shows the model, that I used to build clients for the attachmentAV API.
$version: "2.0"
namespace com.attachmentav.developer
use aws.protocols#restJson1 use aws.api#service
@title("attachmentAV - Virus and Malware Scan API (SaaS)") @restJson1 @httpApiKeyAuth(name: "x-api-key", in: "header") @service(sdkId: "VirusMalwareScanSaaS") service VirusMalwareScanSaaS { version: "2006-03-01" operations: [CreateSyncDownloadScan, CreateSyncBinaryScan] }
However, I ran into two problems with Smithy. On the one hand, Smithy is clearly focused on generating SDKs for AWS services and therefore makes assumptions about endpoint URLs, authentication mechanisms, and more by default. Using Smithy outside these defaults was tricky for me. On the other hand, most client generators are not production-ready yet and are marked as developer preview. That led to generated code that was not compiling or did not work as intended.
OpenAPI Generator: Generate from OpenAPI documents
As we use an OpenAPI document to deploy the API Gateway on AWS, giving OpenAPI Generator a try was pretty simple. All I needed to do was to hand over the OpenAPI document to the generator.
But, the result was not very promising. The usability of the generated SDK was pretty bad.
The following idea brought the turnaround: define a seperate OpenAPI document with the aim of generating an SDK that is easy to use. I removed some resources and methods, modified the resource definitions, and more.
The following snippet shows the OpenAPI document optimized to generate SDKs.
openapi:3.0.0 info: description:'Scan files for viruses, trojans, and other kinds of malware.' title:attachmentAV version:1.0.0 servers: -url:https://eu.developer.attachmentav.com/v1 security: -apiKeyAuth:[] -bearerAuth:[] tags: -name:attachmentAV paths: /scan/sync/binary: post: description:'Upload a file, scan the file, and return the scan result.' tags:[attachmentAV] requestBody: content: application/octet-stream: schema: format:binary type:string required:true responses: '200': content: application/json: schema: $ref:'#/components/schemas/ScanResult' description:Success /scan/sync/download: post: description:'Download a file from a remote location (HTTP/HTTPS), scan the file, and return the scan result.' tags:[attachmentAV] requestBody: content: application/json: schema: $ref:'#/components/schemas/SyncDownloadScanRequest' required:true responses: '200': content: application/json: schema: $ref:'#/components/schemas/ScanResult' description:Success /scan/sync/s3: post: description:'Download a file from S3, scan the file, and return the scan result. A bucket policy is required to grant attachmentAV access to the S3 objects.' tags:[attachmentAV] requestBody: content: application/json: schema: $ref:'#/components/schemas/SyncS3ScanRequest' required:true responses: '200': content: application/json: schema: $ref:'#/components/schemas/ScanResult' description:Success components: schemas: ScanResult: example: size:size realfiletype:realfiletype finding:finding status:status properties: status: type:string finding: type:string size: type:string realfiletype: type:string type:object SyncDownloadScanRequest: properties: download_url: type:string download_headers: additionalProperties: type:string type:object required: -download_url type:object SyncS3ScanRequest: properties: bucket: type:string key: type:string version: type:string required: -bucket -key type:object securitySchemes: apiKeyAuth: in:header name:x-api-key type:apiKey bearerAuth: type:http scheme:bearer
Building SDKs for an API increases discoverability and simplifies integration. Implementing and maintaining SDKs by writing code manually is a huge effort. For us, using the OpenAPI Generator with an optimized OpenAPI document brought the best results.