Software Development

Building a Typesafe API Wrapper

A Clean Solution to a Tedious TypeScript Problem

Building a Typesafe API Wrapper
Generated by DALLE 3
“Type Safety: Where code wears a seatbelt even before hitting the road.”

Today I present to you a lightweight, framework-agnostic API wrapper to guarantee Typesafe data fetching

Requires minimal boilerplate, and it builds upon the standard fetch API, therefore no additional dependencies to bloat your applications.

So without further ado… Lets dive right in.


Creating our Type Definition

Lets begin by creating a simple type, CreateAPIMethod, that will specify the expected inputs and outputs for our API, plus all necessary options to make the API call (e.g. request method, URL).

// The Generic API method should always specify the expected input "T" 
// and output "U" The "opts" include the API url and the request method. 
// You could extend this to accept more options.

// This returns a function that takes in the expected input "T" 
// and returns a Promise for "U" which emulates the functionality 
// of a simple fetch request.

export type CreateAPIMethod = <
  T extends Record<string, string> | undefined,
  U
>(opts: {
  url: string
  method: "GET" | "POST" | "PATCH" | "PUT" | "DELETE"
}) => (input?: T) => Promise<U>

Constructing our Typesafe API Method

Lets define a “constructor” method createAPIMethod to create a Typesafe fetch request template.

const createAPIMethod: CreateAPIMethod = function createAPIMethod(opts) {
  return async function (input) {
    const res = await fetch(opts.url, {
      method: opts.method,
      body: opts.method === "GET" ? undefined : JSON.stringify(input),
    })
    return await res.json()
  }
}

The purpose of createAPIMethod is to create a templated fetch request which is fully Typesafe; similar to a class constructor.

Remember, a GET request cannot have a body, which is why I set the request body as undefined for GET.

We will then define a Typesafe API method getPostBodies to GET data from the jsonplaceholder testing API.

const getPostBodies = createAPIMethod<undefined, { body: string }[]>({
  method: "GET",
  url: "https://jsonplaceholder.typicode.com/posts",
})

Here we construct an API method by passing in undefined input for GET.

We expect the output to "include" an array of { body: string } objects.

Next we pass the request method and API URL.

I chose the jsonplaceholder testing API, but this works on all APIs.

Testing our Typesafe API Method

We can test our Typesafe API method by defining a function printPostBodies to return posts bodies and print them out into an array.

// We create a function to print the bodies of all 
// posts returned from the API.

const printPostBodies = async () => {
  const bodies = await getPostBodies()
  console.log(bodies.map((post) => post.body))
}
printPostBodies()

Here is the expected output when you request the following API endpoint:

GET

First 5 Post Bodies Shown
First 5 Post Bodies Shown

Conclusion

Congratulations!

You now have a portable and lightweight fetch API wrapper that will ensure type safety for every API call you make.


If you enjoyed this article, please make sure to Subscribe, Clap, Comment and Connect with me today! 🌐

References

1