Generate API Clients: A new way to consume REST APIs and GraphQL
Tags: api, client, typescript, graphql
Published: 2024-04-30 15:21:01

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, we can make the development more enjoyable while improving the UX by reducing bundle size and startup time.

**Why generate API clients?**

* **Absolute accuracy:** Generated clients follow specifications such as GraphQL SDL or Swagger OpenAPI to 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:** It takes only a few minutes to generate your API client, which then saves you hours and hours writing functions for each endpoint, manually defining their request params and response payloads. What's more, it saves you the effort of fixing any potential errors by doing so manually. I've been there!
* **Outstanding IDE support:** Witness yourself in the GraphQL section below!


**How to generate API Clients?**

## RESTful APIs via [swagger-typescript-api](https://github.com/acacode/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`:**
	```bash
	npm install -D swagger-typescript-api
	```
   
2. **Generate your client:**
	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:
	```json
	"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 client into `src/services/rest.ts`.
   
3. **Use generated client in your code:**
	```typescript
	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](https://github.com/remorses/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, generating clients 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`:**
	```bash
	npm install -D @genql/cli
	```

2. **Generate your client:**
	```json
	"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 client.

3. **Use generated client in your code:**
	```typescript
	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

Generated TypeScript API clients offer a powerful and efficient way to interact with HTTP APIs, whether you are working with GraphQL or REST. If you're looking to improve your API consumption workflow, I highly recommend giving them a try!