DEV Community

Cover image for Generative HTTP API Clients
Hồng Phát
Hồng Phát

Posted on

Generative HTTP API Clients

Integrating HTTP APIs often involves tedious double-checking:

  • Was the endpoint GET /sheep or GET /sheeps?
  • Should the parameters be put in the URL query string, request body, or even headers?
  • Is the API server expecting to receive parameters in the Content-Type application/json or x-www-form-url-encoded?

By generating HTTP API clients from existing API endpoints using the approaches below, you can leave the double-checking to TypeScript and your IDEs.

From this point forward, I'll use the abbreviation "GHAC" to refer to Generative HTTP API Client(s).

Why use GHACs?

  • Absolute accuracy: Leveraging conventional specifications to generate API clients, GHACs ensure your server receives what it demands. Things can’t go wrong unless you choose to disregard TypeScript declarations by unleashing the power of the any keyword.
  • Long-term productivity: Setting up a GHAC may take about as many hours as writing functions for each endpoint and manually defining their request params and response payloads, but it saves you the effort and potential errors of doing so manually. I've been there!
  • Boilerplate-less: GHACs eliminate the need to write repetitive boilerplate code/typing for each endpoint.
  • Outstanding IDE support: Witness yourself in the GraphQL section below!

How to use GHACs?

RESTful APIs via swagger-typescript-api

As the library name suggests, using this approach requires your RESTful APIs documented using the OpenAPI Specification (formerly Swagger). Server-side frameworks like Java Spring can often generate this documentation for you effortlessly.

If you use other specifications, there might be an equivalent library available.

Steps:

  1. Install swagger-typescript-api:

    npm install -D swagger-typescript-api
    
  2. Generate your GHAC:
    You can either execute the command directly or add a script entry to your package.json file for easy regeneration when your backend APIs change:

    "scripts": {
      "gen:rest": "swagger-typescript-api -p https://your-server.com/rest-api-endpoint/docs -o ./src/services -n rest.ts"
    }
    

    Make sure the -p parameter points to your Swagger schema, usually a JSON/YML endpoint (not the Web UI endpoint). Run npm run gen:rest to generate your GHAC into src/services/rest.ts.

  3. Use GHAC in your code:

    import { Api } from "services/rest";
    
    const client = new Api({
      baseUrl,
      securityWorker, // Use this to set up Bearer tokens in request headers
    });
    
    const { data } = await client.api.getProducts({
      page: 1,
      limit: 10,
      ... // TypeScript will let you know which params can be passed in
    });
    

The best part is that your response data is always strongly typed. This means that whenever you type data. and press Ctrl+Space or + Esc, your IDE will let you know precisely what lives inside your data, fields that you didn't even know existed and fields that you think you could use but, in fact, could not. No verbose code is required!

GraphQL via genql

I actually discovered this mechanism and decided to write a blog about it while working with GraphQL. As someone who embraces the KISS principle, I found working with Apollo clients to be quite daunting: writing GraphQL queries within large JavaScript strings with limited extension support, intricate approach to customizing outgoing requests (such as placing tokens in headers and retrying after token refreshes), or caching behaviour that often differed from expectations.

On the other hand, setting up GHAC for GraphQL is much simpler because all endpoints already adhere to a single specification. Honestly, I believe genql should be the official solution for web GraphQL clients.

Steps:

  1. Install @genql/cli:

    npm install -D @genql/cli
    
  2. Generate your GHAC:

    "scripts": {
      "gen:gql": "genql --endpoint https://your-server.com/graphql/ --output ./src/services/graphql"
    }
    

    Then, you can simply run npm run gen:gql to generate your GHAC.

  3. Use GHAC in your code:

    import { createClient } from "services/graphql";
    
    const client = createClient({
      fetch, // Customize the fetch function to add JWT tokens or implement retry logic
    });
    
    const data = await client.query({
      // IDEs with TypeScript support will assist you with typing all of the code below effortlessly
      products: {
        name: true, // Pick only the fields you want
        __scalar: true, // Or pick all the fields with primitive values
        reviews: {
          rating: true, // Nested picking allowed
        },
      },
    });
    

What is truly impressive: you get a subtype with only the fields you requested. Your IDE will discourage you from typing .content after data.products[0].reviews[0] because it wasn't queried, even though it exists in the user schema. This would require much more verbose code in Apollo clients. Kudos to the genql and TypeScript developers!

Conclusion

GHACs offer a powerful and efficient way to interact with HTTP APIs, especially when combined with TypeScript. If you're looking to improve your API consumption workflow, I highly recommend giving them a try!

Top comments (0)