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

@tonisanchez.dev/init-service

v1.0.4

Published

Communicating with different APIs from the Front End is often a repetitive task that can be summarised in a documented and coded process. In order to save time and use a consistent code style among different services is vital to have this process pre-defi

Downloads

283

Readme

Motivation

Communicating with different APIs from the Front End is often a repetitive task that can be summarised in a documented and coded process. In order to save time and use a consistent code style among different services is vital to have this process pre-defined.

About

A script to generate a folder named as your service, containing a default service with response handlers, including error handlers. Built in TypeScript.

└── my-service
    │
    └── service.ts
    └── utils.ts
    └── index.ts

Installation

npm i -D @tonisanchez.dev/init-service

Alternatively

yarn add -D @tonisanchez.dev/init-service

Assumptions

There is usually 4 main things that the Front End finds useful on an API response in order to handle the response, those are:

  • "data" The front end access the data returned and parse it with a specific function that is also prepared to handle errors.
  • "status" Ideally there is only one status code that can be considered succesful. Not by itself, but this along with the data parsed can be enough to determine if the response can be considered successful.
  • "headers.content-length" sometimes is useful to know how many items are in the response.
  • "headers.content-type" know if the content type of the response is as expected. (application/json, etc.)

Default response validator

Conditions:

  • Status is 200 (201 when adding resources or 204 when deleting)
  • The format (use typescript and/or parser) is exactly as expected.

Functions to use (Functional Programming)

  • isSuccessful(res) A case specific function (ideally not to reuse, since services can evolve and demand different behavior) to determine if the response is successful comparing the response with the expected status code and expected format. (Using previously type coded param as ExpectedParam) Other than that, it would be handled as failed.
  • handleSuccess(res)
  • handleFail(res)

Using this reusable function would have this format

const functionUsingThatService = async () => {
  const res = await getService(params)
  isSuccessful(res)
    ? handleSuccess(res.data)
    : handleFail(res)
}

The isSuccessful function could look something like:

/******************************************************************************
 * Check if the response passed is considered successful.
 * @param {AxiosResponse<GetCountryResponse>} res the response of the API
 * @returns boolean true if response is accepted as successful
 *****************************************************************************/
export const isSuccessful = (res: AxiosResponse<GetCountryResponse>) => {
  const statusExpected = 200
  const matchesExpected = (param: GetCountryResponse) => {
    return param as GetCountryResponse
  }
  return res.status === statusExpected && matchesExpected(res.data)
}

The handleSuccess handler could look like this:

/******************************************************************************
 * @param {AxiosResponse<GetCountryResponse>} res the response of the API
 * @returns triggers a toaster notification
 *****************************************************************************/
export const handleSuccess = (res: AxiosResponse<GetCountryResponse>) => {
  const title =
    (res.data && res.data[0] && res.data[0].region) || 'Fallbacktitle'
  const description =
    (res.data[0] && res.data[0].name && res.data[0].name.common) ||
    'Fallback description'
  toaster.success(title, { description: description })
}

The handleFail handler could look like this:

/******************************************************************************
 * @param {AxiosResponse<GetCountryResponse>} res the response of the API
 * @returns triggers a danger toaster notification and logs error
 *****************************************************************************/
export const handleFail = (res: AxiosResponse<GetCountryResponse>) => {
  if (res.status && res.status === 404) {
    toaster.danger('My custom not found message')
    console.error({ myCustomField: 'My tip', ...res })
  } else {
    toaster.danger('General error message')
  }
}

Another useful and more reusable function would be responseIsExpected:

/******************************************************************************
 * @param {AxiosResponse<GetCountryResponse>} res the response of the API
 * @param {statusCode} number status code of the response
 * @param {formatIsExpected} function the validator of the response format
 * @returns bolean true if the response is as expected
 *****************************************************************************/
export const responseIsExpected = (
  res: AxiosResponse<GetCountryResponse>,
  statusCode: StatusCode,
  formatIsExpected: (params: unknown) => boolean // will be use case specific
) => {
  return res.status && res.status === statusCode && formatIsExpected(res)
}

How it works

Execute the script on your package.json like this:

// package.json
...
  "service": "node node_modules/@tonisanchez.dev/init-service 'YOUR_SERVICES_FOLDER_DIR'",
...

Everytime you call this script a new folder will be created containing 6 files:

  • Default file containing a service with axios.
  • Default file containing functions for response handling, including error handlers.
  • An index to use it optionally for repositories with an folder structure of "index" files.

Parameters

Service name

Using script as shown above, you can specify the name of your service with a following parameter as shown below:

npm run service myService

Alternatively

yarn service myService

Default content of files generated

[service.ts]

import axios from 'axios'
import { base } from '../routes'

export const getThis = async (country: string) => {
  const baseUrl: string = `https://restcountries.com/v3.1/name/\${country}`
  const res = await axios.get(baseUrl).catch((error) => error.response)
  return res
}

[utils.ts]

export type MyServiceProps = {
  children: string
}import { AxiosResponse } from 'axios'
import { toaster } from 'ui-sora'
import { GetCountryResponse, StatusCode } from '../../types'

/******************************************************************************
 * Check if the response passed is considered successful.
 * @param {AxiosResponse<GetCountryResponse>} res the response of the API
 * @returns boolean true if response is accepted as successful
 *****************************************************************************/
export const isSuccessful = (res: AxiosResponse<GetCountryResponse>) => {
  const statusExpected = 200
  const matchesExpected = (param: GetCountryResponse) => {
    return param as GetCountryResponse
  }
  return res.status === statusExpected && matchesExpected(res.data)
}

/******************************************************************************
 * @param {AxiosResponse<GetCountryResponse>} res the response of the API
 * @returns triggers a toaster notification
 *****************************************************************************/
export const handleSuccess = (res: AxiosResponse<GetCountryResponse>) => {
  const title =
    (res.data && res.data[0] && res.data[0].region) || 'Fallbacktitle'
  const description =
    (res.data[0] && res.data[0].name && res.data[0].name.common) ||
    'Fallback description'
  toaster.success(title, { description: description })
}

/******************************************************************************
 * @param {AxiosResponse<GetCountryResponse>} res the response of the API
 * @returns triggers a danger toaster notification and logs error
 *****************************************************************************/
export const handleFail = (res: AxiosResponse<GetCountryResponse>) => {
  if (res.status && res.status === 404) {
    toaster.danger('My custom not found message')
    console.error({ myCustomField: 'My tip', ...res })
  } else {
    toaster.danger('General error message')
  }
}

/******************************************************************************
 * @param {AxiosResponse<GetCountryResponse>} res the response of the API
 * @param {statusCode} number status code of the response
 * @param {formatIsExpected} function the validator of the response format
 * @returns bolean true if the response is as expected
 *****************************************************************************/
export const responseIsExpected = (
  res: AxiosResponse<GetCountryResponse>,
  statusCode: StatusCode,
  formatIsExpected: (params: unknown) => boolean // will be use case specific
) => {
  return res.status && res.status === statusCode && formatIsExpected(res)
}

Have fun with it

I will be publishing newer versions and extend this simple and small tool. Meanwhile, I have more interesting projects on my Github @TonySapa or site tonisanchez.dev