Typical data fetching examples
- This is the typical way of fetching data in Typescript using the
Fetch API
:
const response = await fetch("https://example.com/movies");
const jsonData = await response.json();
Then we need to cast the jsonData
, because it's type is any:
const movies = jsonData as Movies;
- Some people create a generic wrapper around
fetch
:
async function request<TResponse>(
url: string,
config: RequestInit
): Promise<TResponse> {
const response = await fetch(url, config);
return await response.json();
}
And then fetching the data looks like this:
const movies = await request<Movies>("https://example.com/movies");
The problem
This is absolutely fine, however if your API is big enough, you're left with a giant redundant code - you need to configure the method (POST, PUT, DELETE), then stringify the body / add query parameters ect.
We can utilize some Typescript magic to help us with that.
I created a ts-rest-api-client
repository which contains full documentation and an example with Node.js.
The API of this client looks like this:
// GET /movies
const movies = await client("/movies").get();
It's type-safe, so you don't need to do any type casting!
Also, the urlPath
is also fully-typed:
If you misstype the urlPath
, you will receive Typescript error.
Path parameters are also supported:
// GET /posts/1
await client("/movies/{id}", "1").get();
You can also use queryParameters:
// GET /comments?postId=1
const comments = await client("/comments").get({
queryParameters: {
postId: 1,
},
});
Other HTTP methods (POST, PUT, PATCH, DELETE) - are also supported:
// POST /posts
await client("/posts").post({
body: {
title: "foo",
body: "bar",
userId: 1,
},
});
^^^ body
will be automatically serialized to JSON under the hood.
You can also extend this client with auth
.
By default, JWT Bearer
is supported.
Just change extended interface in http/requests.ts
from QueryOptions
(or Mutation Options
to AuthQueryOptions
(or AuthMutationOptions
):
export interface CreatePostOptions extends AuthMutationOptions {
body: {
title: string;
body: string;
userId: number;
};
}
// POST /posts
await client("/posts").post({
body: {
title: "foo",
body: "bar",
userId: 1,
},
jwtToken: "", // Add your JWT Token here
});
Defining your endpoints schema is simple:
export interface EndpointsSchema {
"/posts": {
get: () => Promise<Post[]>;
post: (options: CreatePostOptions) => Promise<Post>;
};
"/posts/{id}": {
get: () => Promise<Post>;
put: (options: PutPostOptions) => Promise<Post>;
patch: (options: PatchPostOptions) => Promise<Post>;
delete: () => Promise<void>;
};
"/comments": {
get: (options: GetCommentsByPostIdOptions) => Promise<Comment[]>;
};
"/posts/{id}/comments": {
get: () => Promise<Comment[]>;
};
}
Full documentation is available here.