Developing a Library for Managing APIs in TypeScript

I'm currently struggling to figure out how to code this API wrapper library. I want to create a wrapper API library for a client that allows them to easily instantiate the lib with a basePath and access namespaced objects/classes with methods that call into the internal API. The goal is to simplify the process for the consumer by only requiring them to pass in necessary data for each API call, while also handling errors generically.

I understand that what I have here won't work as is, but it gives an idea of my desired outcome.

Some questions I have are:

  • Should the basePath be set from a static method and just be a property?
  • How else could this be structured so we can share methods and access to the basePath without needing to reinstantiate it each time?

Any help and suggestions are greatly appreciated.

abstract class Base { 
    constructor(private basePath: string = '') {}
    async get<T>(endpoint: string, config?: RequestInit | undefined): Promise<T> { 
        const response = await fetch(this.basePath + endpoint, config);
        return response.json();
    }
    // ... other available methods  
}

// Exported for ease of use. Other examples could include `Comments.postNewComment({user, data})`
class Posts extends Base {
  getAllPosts() {
    return this.get<PostsData>('/posts')
  }
}

// Main index file for lib would export all classes with available API methods (Posts, Auth, Comments etc) for the consumer to use.
// An issue arises when creating an instance because it requires the basePath again
export { Posts: new Posts(), Comments: new Comments() }; 

// Ideal usage: Main app file:
import LibName from 'lib-name'
new LibName({ basePath: 'api.whatever' }) // Simplified version where we instantiate the basePath and anything else

// In various locations throughout the app, different sections can call the methods (Auth.login, Comments.addNewComment etc)
import { Posts } from 'lib-name';
Posts.getAllPosts();

I initially followed this article, but the final example exposes ALL methods under one export (DevToClient.X), whereas I prefer them namespaced under their appropriate parent object.

Answer №1

Is it a good idea to create a customizable class for an Endpoint based on the object type T?

class Endpoint<T extends {id: string}, Creatable = T> { 
    constructor(private endpointPath: string) {}
    async get(id: string, config?: RequestInit | undefined): Promise<T> { 
        const response = await fetch(this.endpointPath + '/' + id, config);
        return response.json();
    }
    // stubs of sample methods
    async getAll( someFilters?: any ): Promise<T[]> {}
    async update(changes: Partial<T> & {id: string}): Promise<T> {}
    async createNew( object: Creatable ): Promise<T> {}
}

The reason for adding the optional argument Creatable is because not all properties of the created object are included in a typical create request. For example, id and date might be set by the server.

Instead of exporting the Endpoint class directly, instances would be created for different types and then exported.

export const Posts = new Endpoint<PostObject>('/posts');
export const Comments = new Endpoint<Comments>('/comments');

To use it:

import {Posts} from 'some-lib'

const post = Posts.get("321"); // Promise<PostObject>

// arguments vary depending on your api structure
const latest = Posts.getAll({limit: 10, sort: 'date'}); // Promise<PostObject[]>

const updated = Posts.update({id: "123", title: "New Title"}); // Promise<PostObject>

If you need more customization options like validation that differ between endpoints, consider using composition. The constructor of Endpoint can accept a config object or class for this purpose.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Implementing swipe functionality to Bootstrap carousel

This is the code snippet I am working with: <div class="container"> <div id="myCarousel" class="carousel slide" data-ride="carousel" data-interval="3000"> <!--Indicators--> <ol class="carousel-indicators"> ...

There was an error in parsing the JSON data due to an unexpected token "u" at the beginning of the string

I've been working on improving my JavaScript skills, but I hit a snag with an error message that reads "Uncaught SyntaxError: Unexpected token u in JSON at position 0 at JSON.parse". var requestData = new XMLHttpRequest(); requestData.open('GET& ...

What is the best way to retrieve data input from my select dropdown?

I have a select menu generated from a database and I want to display the value of the selected option in an input field. Currently, my code only shows the value of the first option. How can I modify it to display all selected values? Thank you. <?php ...

Angular's ng-include functionality allows developers to dynamically load

As a beginner in angular.js, I've written my first 'hello world' script. I'm attempting to use ng-include to bring in a navbar from the local file /templates/nav.html. Despite following a tutorial closely, I'm struggling to underst ...

Error encountered in TypeScript's Map class

When working with TypeScript, I keep encountering an error message that reads "cannot find name Map." var myMap = new Map(); var keyString = "a string", keyObj = {}, keyFunc = function () {}; // assigning values to keys myMap.set(keyString, "val ...

Obtaining the complete JSON array in string format

I am currently using Fine Uploader to pass parameters in this way callbacks: { onSubmit: function(id, fileName) { this.setParams({ a: 'adm', b: '126', c: { fileID: id, ...

Can React JSX and non-JSX components be combined in a single project?

I'm currently faced with a question: If I have a parent component in JSX but need to include an HTML tag like <i class="fa fa-window-maximize" aria-hidden="true"></i>, which is not supported by React JSX (correct me if I'm wrong), can ...

Tips for transferring the id from the url to a php function seamlessly without causing a page refresh

I have a div that includes a button (Book it). When the button is clicked, I want to append the id of the item I clicked on to the current URL. Then, use that id to display a popup box with the details of the clicked item without refreshing the page, as it ...

Ways to determine if your code is written in ES6

After completing a lot of Javascript coding, I just learned about the existence of various JS 'versions' such as ES5 and ES6. My project is now up on Github, and someone advised me to convert my ES6 code to ES5 using Babel. The problem is, I&ap ...

The issue with ng-if not functioning within ng-repeat is that the ng-if directive

My issue is with using ng-if inside an ng-repeat in AngularJS. Despite updating to the latest version on 09/27/2014, I am still unable to make it work properly. The code functions perfectly outside of ng-repeat, and also works fine inside ng-repeat when us ...

The JavaScript animations in AngularJS using ng-class are not being activated

I've been attempting to apply js-defined animations to the ng-class directive using the standard syntax of add and remove, but for some reason, the animations are not running. After checking the logs, it seems that the add and remove functions are not ...

Fresh ajax requests are not clearing the current data displayed in the JSP table

My ajax function retrieves data from a servlet and displays it in the page successfully. However, each time a new ajax call is made, the form appends the new data to the existing results instead of replacing them. I need to reset the current values stored ...

Tutorial on integrating jquery ui's '.droppable' feature with the jQuery '.on' method

I have a main div with three inner divs labeled 1, 2, and 3 as shown below: <div id="main"> <div style="height:30px; background-color:yellow;" class="bnr">Banner</div> <div style="height:30px; background-color:yellow;" class=" ...

When switching from JavaScript to jQuery, the button values become invisible

Currently, I have a functional app that can dynamically change the values of buttons based on user input. The current implementation is in vanilla JavaScript within the script.js file. However, I am looking to enhance the functionality and user experience ...

How to retrieve the outcome of a stored procedure using node.js

Is it possible to retrieve multiple select results from distinct tables (many rows) in just one stored procedure in mysql and then access those results in nodejs? In .NET with SQL Server, we can use "sqlnextresult" for this purpose. VIEW IMAGE FROM STORE ...

AngularJS to validate image dimensions (width and height) on upload

Hello, I am new to working with Angular JS. I have been experimenting with a login form that includes a file upload field for the user picture. The input field code looks like this: <input type="file" file-model="userPic"/> When I submit the form i ...

What is the best way to test chained function calls using sinon?

Here is the code I am currently testing: obj.getTimeSent().getTime(); In this snippet, obj.getTimeSent() returns a Date object, followed by calling the getTime() method on that Date. My attempt to stub this functionality looked like this: const timeStu ...

Add fresh inline designs to a React high-order component creation

Applying a common HOC pattern like this can be quite effective. However, there are instances where you may not want a component to be wrapped, but rather just extended. This is the challenge I am facing here. Wrapper HOC const flexboxContainerStyles = { ...

Implementing Checkbox Functionality within a Dropdown Menu using AngularJS or JavaScript

I am interested in finding a multi-select checkbox solution similar to the one demonstrated here: Instead of using jQuery, I would prefer options in AngularJS or pure JavaScript. Does such a solution already exist in these frameworks, or is there guidance ...

Arrange an array by the occurrence rate of its elements within objects contained in another array, using Javascript

I found the explanation to be rather complex. Essentially, my goal is to iterate through each object in the 'objects' array, analyze the 'choice' values, tally the frequency of each letter, and then arrange the original arrays based on ...