Search code examples
typescriptinterface

Strongly-typed Rest client implementation with Typescript


I have this interface:

export interface WorkoutLibrary {
"/Workout": {
    get: (
        input?: GetWorkoutListOptions
    ) => Promise<GetWorkoutList200Response | ErrorResponse>;
    post: (
        input: CreateWorkoutOptions
    ) => Promise<CreateWorkout201Response | ErrorResponse>;
    put: (
        input: UpdateWorkoutOptions
    ) => Promise<UpdateWorkout200Response | ErrorResponse>;
};
"/workout/{workoutId}": {
    get: (
        input?: GetWorkout200Response
    ) => Promise<GetWorkout200Response | ErrorResponse>;
    delete: (
        input?: DeleteWorkoutOptions
    ) => Promise<DeleteWorkout200Response | ErrorResponse>;
}

}

The problem is I do not know how to implement this to make actual calls to my API. Can someone provide an example on how to actually implement this in a class to use, let's say Axios, to make the API calls?

Probably I am missing some key Typescript concepts here, but I am kind of new to it.
If someone could point me in the right direction, I would really appreciate it.


Solution

  • To implement this type, you just make an object that matches those property and function types:

    For simplicity, let's focus on one:

    export interface WorkoutLibrary {
      "/Workout": {
        get: (input?: GetWorkoutListOptions) =>
          Promise<GetWorkoutList200Response | ErrorResponse>;
      }
    }
    

    Now your question is missing some types, so let's say those are lie this:

    type ErrorResponse = { error: string }
    
    type GetWorkoutListOptions = { type: 'arms' | 'legs' | 'core' }
    type GetWorkoutList200Response = { name: string }[]
    

    Now you can make an implementation like this:

    const workoutLibrary: WorkoutLibrary = {
      '/Workout': {
        get: async (input) => {
          try {
            const response = await fetch(
              `http://example.com/Workout?type=${input?.type ?? 'all'}`
            )
    
            const json = await response.json()
            return json
          } catch (error) {
            return { error }
          }
        }
      }
    }
    

    This creates an object that matches the WorkoutLibrary type and its functions. It uses fetch() to fetch a url, and the input is used in that URL. If an error occurred, it returns something in the shape of ErrorReponse.

    And then you can call this function like so:

    await workoutLibrary['/Workout'].get({ type: 'core' })
    

    See Playground


    Note, there are many ways to achieve this, and may libraries you could use. This code should merely be thought of as an example and a starting point.