DEV Community

Cover image for I Build Javascript/Typescript API wrapper
Madhav Majumdar
Madhav Majumdar

Posted on

I Build Javascript/Typescript API wrapper

Yes I build a wrapper, and this age of AI building something like this maybe not an achievement , but if you are a Developer , You have every right to build anything , and nobody can stop you 

Now, back to the story, building a wrapper SDK around an API is very simple; you need TWO things for that 

  • Request
  • Response

Simple isnt it?
Now I build it using Typescript, Reason: typesafety, you can build using JavaScript, but I would recommend using zod for that

Request

A message sent to the server by the client to perform an action or get information
example :

curl --request GET \
  --url http://localhost:3773/agent/skills/{skillId}
Enter fullscreen mode Exit fullscreen mode

Response

A message sent by the server replying the request made by client
example:

{
  "id": "pdf-processing-v1",
  "name": "pdf-processing",
  "description": "Extract text, fill forms, and extract tables from PDF documents.\nHandles both standard text-based PDFs and scanned documents with OCR.\n",
  "tags": [
    "pdf",
    "documents",
    "extraction",
    "forms",
    "tables"
  ],
  "input_modes": [
    "application/pdf"
  ],
  "output_modes": [
    "text/plain",
    "application/json",
    "application/pdf"
  ],
  "version": "1.0.0",
  "examples": [
    "Extract text from this PDF document",
    "Fill out this PDF form with the provided data",
    "Extract tables from this invoice PDF"
  ],
  "performance": {
    "avg_processing_time_ms": 2000,
    "max_file_size_mb": 50,
    "max_pages": 500,
    "concurrent_requests": 5
  },
  "allowed_tools": [
    "Read",
    "Write",
    "Execute"
  ],
  "has_documentation": true
}
Enter fullscreen mode Exit fullscreen mode

By looking on request and response, you can build :

  1. A class with a methods
  2. Types and abstraction

This way, we can wrap the functionality and property, and expose only the required content to the client 
But first, imagine if you are the client, what you want when you code
By looking at the following example:

const client = new SkillClient({ url: "http://localhost:3773" });

await client.agent.getSkillById("<skill-id>")
Enter fullscreen mode Exit fullscreen mode

Now we have a clear picture, so let's start building. First:

Abstraction and Type

  1. Type

Start with a request Type, We can see it has only one type, string in request URL endpoint, we can leave it, but I would like to have a Type for everything

export interface GetSkillParams {
  skillId: string;
}
Enter fullscreen mode Exit fullscreen mode

And return a response, which also needs a Type

export interface SkillDetail {
  id: string;
  name: string;
  description: string;
  tags: string[];
  input_modes: string[];
  output_modes: string[];
  version: string;
  examples: string[];
  capabilities_detail: Record<string, unknown>;
  performance: SkillPerformance;
  allowed_tools: string[];
  has_documentation: boolean;
}

export interface SkillPerformance {
  avg_processing_time_ms: number;
  max_file_size_mb: number;
  max_pages: number;
  concurrent_requests: number;
}
Enter fullscreen mode Exit fullscreen mode
  1. Abstraction Now, let's build an abstraction for implementing in class. For abstraction, we can use an abstract class But in Typescript, we have interface which can be better for a simplified version
export interface SkillsType {
  getSkill(params: GetSkillParams): Promise<SkillDetail>;
}
Enter fullscreen mode Exit fullscreen mode

Class and Method

Let's build a class and methods that will be exposed to the client

export class SkillsClient implements SkillsType {
  private readonly baseUrl: string;

  constructor(url: string) {
    this.baseUrl = url;
  }

  async getSkillById(params: GetSkillParams): Promise<SkillDetail> {
    const url = new URL(`/agent/skills/${params.skillId}`, this.baseUrl).toString();

    let response: Response;

    try {
      response = await fetch(url, { method: "GET" });
    } catch (error) {
      throw new NetworkError(0, "Failed to reach the skill detail endpoint.", error);
    }

    if (!response.ok) {
      const json = (await response.json()) as SkillErrorResponse;
      throw new SkillDetailError(response.status, json.error, json.details);
    }

    return (await response.json()) as SkillDetail;
  }
}
Enter fullscreen mode Exit fullscreen mode

In my case, I have defined specific Error type , so I build an custom error, It is convenient to have a custom error directory

export interface SkillErrorResponse {
  error: string;
  details?: string;
}
export abstract class BaseError extends Error {
  constructor(
    public code: number,
    message: string,
    public data?: unknown,
  ) {
    super(message);
    this.name = "BaseError";
  }
}
export class NetworkError extends BaseError {
  constructor(code: number, message: string, data?: unknown) {
    super(code, message, data);
    this.name = "NetworkError";
  }
}
Enter fullscreen mode Exit fullscreen mode

And that's all : )
Now export everything from the main index file

export * from "./skill"
export * from "./type"
export * from "./error"
Enter fullscreen mode Exit fullscreen mode

and define your export in package json file

{
  "name": "@skill/core",
  "version": "0.1.0",
  "description": "TypeScript SDK for skill",
    "scripts": {
    "build": "tsup",
    "dev": "tsup --watch",
    "lint": "eslint src --ext .ts",
    "typecheck": "tsc --noEmit"
  },
 "module": "./dist/index.mjs",
 "types": "./dist/index.d.ts",
 "exports": {
    ".": {
      "import": "./dist/index.mjs",
      "require": "./dist/index.js",
      "types": "./dist/index.d.ts"
    },
  "dependencies": {},
  "devDependencies": {
    "typescript": "^5.4.0",
    "vitest": "^1.0.0",
    "@types/node": "^20.0.0"
  },
  "keywords": ["agent", "sdk", "typescript"]
}
Enter fullscreen mode Exit fullscreen mode

now publish your package 

https://dev.to/leopold/build-and-publish-your-npm-package-48mb
https://docs.npmjs.com/creating-and-publishing-scoped-public-packages/

And now you are an official Software Developer.

just kidding we need to learn more

Happy coding

Top comments (0)