npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

api-entity

v1.5.1

Published

Create service entities according to your API agnostic to the framework you are using

Downloads

25

Readme

api-entity

Create service entities according to your API agnostic to the framework you are using full documentation.

Problem

Nowadays, our FrontEnd applications use countless endpoints, despite dividing them into multiple layers or entities, but there is still a problem because of the quantity of boilerplate we need to write, bind the URL, create methods, pass parameters, and so on. Therefore, the following implementation mostly removes these problems through an intuitive API.

Usage

Installation

Use the following command line to add this dependency in your app.

yarn add api-entity

Native request

Using native requests and passing directly the path, It works with GET methods.

import { createServiceEntity } from "api-entity";

const posts = createServiceEntity({
  baseUrl: "https://jsonplaceholder.typicode.com",
  entity: "posts",
  actions: {
    all: "/",
    byId: "/:id",
    byIdComments: "/:id/comments",
    postComments: async ({ actions, params }) => {
      const post = await actions.byId(params);
      const comments = await actions.byIdComments(params);
      return {
        post,
        comments,
      };
    },
  },
});

posts.all().then(console.log);
posts.byId({ id: 1 }).then(console.log);
posts.byIdComments({ id: 2 }).then(console.log);
posts.postComments({ id: 3 }).then(console.log);

Using external adapter

Using axios as an adapter or if you prefer another, you'll be able to do it.

import axios from "axios";
import { createServiceEntity } from "api-entity";

// Custom service with axios
const placeholderService = axios.create({
  baseURL: "https://jsonplaceholder.typicode.com",
});

const posts = createServiceEntity({
  entity: "posts",
  actions: {
    byId: {
      path: "/:id",
    },
    all: {
      path: "/",
      resolve: (value) => value.data,
    },
    create: {
      path: "/",
      type: "post",
    },
    update: {
      path: "/:id",
      type: "put",
    },
    delete: {
      path: "/:id",
      type: "delete",
    },
  },
  adapter: placeholderService,
});

posts.all().then(console.log);
posts.byId({ id: 1 }).then(console.log);

API

entity

Name the base URL that will be attached to the action's path. Let's see some examples below.

  • posts:
    • /posts
    • /posts/1
    • /posts/1/comments
  • comments:
    • /comments
    • /comments/top

baseUrl

Base URL of the endpoint; when you create this one, this will be automatically attached alongside the entity and action's path properties automatically.

Let's take a handful of the initial example and see how that resolves it.

const posts = createServiceEntity({
  baseUrl: "https://jsonplaceholder.typicode.com",
  entity: "posts",
  actions: {
    all: "/", // Internally create the path: https://jsonplaceholder.typicode.com/posts/
  },
});

If you are using an adapter, you don't require adding that property 🤓.

actions

Here, actions are part fundamental to modeling our API requests indeed.

Each action corresponds to a specific endpoint, likewise, these can be declared in three distinct manners.

  1. Using the path
const posts = createServiceEntity({
  actions: {
    all: "/",
    byId: "/:id",
    summary: "/summary",
    byIdComments: "/:id/comments",
  },
});
  1. Using an object customizable
const posts = createServiceEntity({
  actions: {
    all: {
      path: "/", // You don't need to add the type "get", default it is
    },
    add: {
      path: "/",
      type: "post",
    },
    byIdComments: {
      path: "/:id/comments",
      // Optional whether you want to manipulate the response and return a new data
      resolve: (value) => value.data,
    },
  },
});
  1. Creating functions for complex manipulations
const posts = createServiceEntity({
  actions: {
    postComments: async ({ actions, params }) => {
      const post = await actions.byId(params);
      const comments = await actions.byIdComments(params);
      return {
        post,
        comments,
      };
    },
  },
});

abort

If you desire to abort some actions for some reason, executing them is pretty straightforward.

To revoke a request, you have the property "abort", followed by the method's name.

const posts = createServiceEntity({
  actions: {
    all: "/",
    byId: "/:id",
  },
});

posts.all().then(console.log);
posts.abort.all(); // abort request of "all" method
posts.byId({ id: 1 }).then(console.log);
posts.abort.byId(); // abort request of "byId" method

action utils

Another alternative to avoid creating nested structures with the type and URL is using these functions.

| action | type | | ------ | ------ | | get | GET | | post | POST | | put | PUT | | patch | PATCH | | del | DELETE |

Here I'm using the example above by replacing the object configuration with helper functions.

import { put, post, get } from "api-entity";

const posts = createServiceEntity({
  actions: {
    all: get("/"),
    update: put("/:id"),
    add: post("/"),
    byIdComments: get("/:id/comments", (value) => value.data),
  },
});

adapter

If you are usually accustomed to using an external library like Axios, you can pass the instance to the adapter property, and it will be resolved internally.

Using types

Actions automatically will resolve async functions with generic types. But it's entirely possible to pass a custom type or interface to overwrite the default definitions.

import { put, post, get } from "api-entity";

type Params = { id: string };
type Payload = {
  title: string;
};

interface PostService {
  all(): Promise<object[]>;
  byId(params: Params): Promise<object>;
  update(params: Params & Payload): Promise<object>;
  add(payload: Payload): Promise<object>;
}

const posts = createServiceEntity<PostService>({
  actions: {
    all: get("/"),
    byId: get("/:id"),
    update: put("/:id"),
    add: post("/"),
  },
});

configureServiceEntity

Another approach to getting a centralized store is using a configuration that wraps multiple entities and resolves them with the same adapter, base URL, or global settings. The entities will be instanced once created this function and won't be reflected on performance.

Each entity's property will be used as the entity property. Therefore, you can use it by accessing through the same name and calling their methods.

import { post, get, createServiceEntity, configureServiceEntity } from "api-entity";

const posts = createServiceEntity({
  actions: {
    all: get("/"),
    byId: get("/:id"),
    add: post("/"),
  },
});

const users = createServiceEntity({
  actions: {
    all: "/",
    byId: "/:id",
  },
});

const services = configureServiceEntity({
  baseUrl: "https://jsonplaceholder.typicode.com",
  entities: { posts, users },
});

// https://jsonplaceholder.typicode.com/users/
services.users.all().then(console.log);

// https://jsonplaceholder.typicode.com/posts/
services.posts.all().then(console.log);

View a full example here 👈