An SDK (Software Development Kit) is a collection of software development tools in one installable package. An SDK can include libraries, APIs, and sample code for a specific platform or programming language. SDKs are used to help developers build applications for a particular platform. In this article, we will go through the steps of building an SDK using TypeScript.
Getting Started with TypeScript and Node.js
TypeScript is a superset of JavaScript that adds optional type annotations and class-based object-oriented programming to the language. TypeScript is easy to adopt for those familiar with JavaScript.
Node.js is a popular platform for building server-side applications. It's fast and efficient, making it ideal for building an SDK.
You can install TypeScript in your local development environment by running the following command:
npm install -g typescript
Setting up Repository and Configuration
So, Let's start with the implementation.
Let's initialize our node js project using npm init -y
Install the necessary dependencies:
npm install --save-dev microbundle
npm install isomorphic-unfetch
microbundle
is a zero-configuration bundler for small libraries and web components. It is used to package and distribute your code in a format that can be used in different environments.
isomorphic-unfetch
is a library that implements the browser's Fetch API to be used on both the client and server. This library allows you to make HTTP requests in a way that is consistent across both client and server-side JavaScript.
Next, you need to initialize TypeScript using the following command:
tsc --init
This will create a tsconfig.json
file in your project, which is the configuration file for TypeScript. You can use this file to specify the options for your TypeScript compiler.
Ensure you modify the tsconfig.json
to have the below values
{
"compilerOptions": {
"module": "commonjs",
"declaration": true,
"removeComments": true,
"allowSyntheticDefaultImports": true,
"target": "es2017",
"sourceMap": false,
"outDir": "./dist",
"baseUrl": "./",
"incremental": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "test", "lib", "**/*spec.ts"]
}
Code Implementation
Create folders and files as per the below structure
sdk/
├── src/
│ ├── resources/
│ │ ├── base.ts
│ │ ├── posts/
│ │ │ ├── index.ts
│ │ │ └── types.ts
│ │ └── users/
│ │ ├── index.ts
│ │ └── types.ts
│ └── index.ts
├── tsconfig.json
└── package.json
Base.ts
// src/resources/base.ts
import fetch from 'isomorphic-unfetch';
type Config = {
apiKey: string;
baseUrl?: string;
};
export abstract class Base {
private apiKey: string;
private baseUrl: string;
constructor(config: Config) {
this.apiKey = config.apiKey;
this.baseUrl = config.baseUrl || 'https://jsonplaceholder.typicode.com';
}
protected request<T>(endpoint: string, options?: RequestInit): Promise<T> {
const url = `${this.baseUrl}${endpoint}`;
const headers = {
'Content-Type': 'application/json',
'api-key': this.apiKey,
};
const config = {
...options,
headers,
};
return fetch(url, config).then((response) => {
if (response.ok) {
return response.json();
}
throw new Error(response.statusText);
});
}
}
The Base
class is an abstract class that provides common functionality for all resources in our SDK. It takes in a Config
object with apiKey
and baseUrl
properties in its constructor which is common and needed for API invocation. The request
the method is used to make HTTP requests to the API. This method takes in an endpoint and an optional RequestInit
the object for additional configuration. The method returns a Promise with the response data of the type T
.
Resource Types and Implementation
Next, let's create the respective resources like posts
, users
along with their types in the resources/{resource} folder. Below is an example of posts
.
Follow similar implementation for other resources as needed.
// src/resources/posts/types.ts
export declare type Post = {
id: number;
title: string;
body: string;
userId: number;
};
export declare type NewPost = {
title: string;
body: string;
userId: number;
};
// src/resources/posts/index.ts
import { Base } from '../base';
import { NewPost, Post } from './types';
const resourceName = 'posts';
export class Posts extends Base {
getPostById(id: number): Promise<Post> {
return this.request(`/${resourceName}/${id}`);
}
getPosts(): Promise<Post[]> {
return this.request(`/${resourceName}`);
}
createPost(newPost: NewPost): Promise<Post> {
return this.request(`/${resourceName}`, {
method: 'POST',
body: JSON.stringify(newPost),
});
}
}
The types.ts
file contains the TypeScript interfaces for resources likePost
, NewPost
. These objects will be used throughout the SDK. These interfaces provide a blueprint for what kind of data each object should contain.
Entry Point: index.ts
// src/index.ts
import { Posts } from './resources/posts';
import { Users } from './resources/users';
export class Library {
posts: Posts;
users: Users;
constructor(config: { apiKey: string; baseUrl?: string }) {
this.posts = new Posts(config);
this.users = new Users(config);
}
}
This file is the entry point of the SDK. It exports a Library
class that acts as a central point of access to all the resources. The constructor of this class takes a config
object that contains the apiKey
and baseUrl
. The apiKey
is used for authentication and the baseUrl
is used to specify the base URL for the API endpoints. This class creates instances of the Posts
and Users
classes and makes them available as properties.
Package JSON
In the package.json
, add the main
, module
, unpkg
and types
as shown below. Also, add build within the scripts object to remove the earlier dist folder and generate the resultant files using the microbundle
library.
{
"name": "sdk",
"version": "1.0.0",
"description": "",
"main": "dist/index.js",
"module": "dist/index.m.js",
"unpkg": "dist/index.umd.js",
"types": "dist/index.d.ts",
"scripts": {
"build": "rm -rf dist && microbundle --tsconfig tsconfig.json --no-sourcemap"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"microbundle": "^0.15.1"
},
"dependencies": {
"isomorphic-unfetch": "^4.0.2"
}
}
Build and Generate the final files
npm run build
This will use the microbundle
library to create different formats of the code in the dist
folder.
Publish to NPM Registry
Create an account on NPM Registry.
Login to your npm account using the following command:
npm login
In the package.json, add the below fields
{ ... "exports": { "require": "./dist/index.js", "default": "./dist/index.modern.js" }, "files": [ "dist" ] }
To publish to the registry, run
npm publish
Test SDK
Install the package from the registry. If not published, you can also make use of `npm link <path_to_sdk> within your node-based test repository.
You can consume using the below:
import { Library } from '<npm_repo>';
const client = new Library({
apiKey: 'your-api-key',
baseUrl: 'https://jsonplaceholder.typicode.com',
});
client.posts
.createPost({
body: 'Lorem Epsum',
title: 'How to ?',
userId: 1,
})
.then((p) => {
console.log(`Created new post with id ${p.id}`);
});
client.users.getUserById(1).then((a) => console.log(a));
Conclusion
Building an SDK using TypeScript is a great way to package and reuse functionality. By creating a set of entities that can be used together, you can provide a convenient and easy-to-use interface for your users. With TypeScript, you can provide type definitions and ensure that the code is more maintainable and easier to debug.
In conclusion, we have covered the steps on how to create an SDK using TypeScript and how to publish it to the npm registry. We hope you found this article helpful. If you liked it, please consider following, liking, and sharing it with your friends and colleagues. Your support helps us create more valuable content for you.