Skip to main content
Version: 6.5

Define API Endpoint

Endpoints describe an asynchronous API. This includes both runtime behavior as well as (optionally) typing.

interface Todo {
userId: number;
id: number;
title: string;
completed: boolean;
interface Params {
id: number;

const fetchTodoDetail = ({ id }: Params): Promise<Todo> =>
fetch(`${id}`).then(res =>

const todoDetail = new Endpoint(fetchTodoDetail);
Example Usage
console.log(await todoDetail({ id: '1' }));
"userId": 1,
"id": 1,
"title": "delectus aut autem",
"completed": false

We will likely want to use this endpoint in many places with differing needs. By defining a reusable function of just the network definition, we empower its use in any context.

This is especially useful when we start adding more information related to the endpoint. For instance, TypeScript definitions help us avoid common mistakes, typos and speed up development with autocomplete.

By tightly coupling the interface definition, while loosely coupling its usage, we reduce boilerplate, complexity, and common mistakes, while increasing performance and enabling global application consistency and integrity even in the face of unreliable asynchronous data.

More than just a function

In addition to an async function and (optional) types, Endpoints are objects, allowing them to provide any additional relevant information about the endpoint itself.

For instance, to allow integration into a cache as well as knowing when to recompute and/or refetch when parameters change, Endpoints have a key() member that serializes the endpoint and parameters to a unique string.

console.log(todoDetail.key({ id: '1' }));
// fetchTodoDetail {"id":"1"}


The second optional arg is an object to initialize the endpoint with. By avoiding arrow functions, we can use this to access other members we defined.

const todoDetailWithCustomizedKey = new Endpoint(fetchTodoDetail, {
key({ id }) {
return `${this.endpointIdentifier}/${id}`;
endpointIdentifier: 'todoDetail',
console.log(todoDetailWithCustomizedKey.key({ id: '1' }));
// todoDetail/1


For convenience, extend() allows type-correct prototypical inheritance extensions of an endpoint.

This is greatly reduces boilerplate when strong patterns are established for an API like authentication.

Here we show the benefits of customizing method member.

const fetchTodoDetail = function ({ id }) {
return fetch(`${this.urlBase}/todos/${id}`, { method: this.method }).then(
res => res.json(),

const todoDetail = new Endpoint(fetchTodoDetail, {
method: 'GET',
urlBase: '',
const todoCreate = todoDetail.extend({ method: 'POST' });
const todoUpdate = todoDetail.extend({ method: 'PUT' });