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

response-state.js

v1.1.0

Published

JavaScript version of the ResponseState pattern

Downloads

4

Readme

ResponseState.JS

Circle CI Dependency Status Coverage Status

JavaScript version of the ResponseState pattern.

The ResponseState pattern improves structure and control flow of business logic by providing

  • a clean representation of the various outcomes that complex operations can yield, and
  • an efficient control flow mechanism to react to those outcomes.

These conventions allow for a clean separation of the different layers within an application stack, through fully domain-specific APIs, while avoiding the need to abuse errors or exceptions for fine-grained, algorithmic control flow.

Using the ResponseState pattern

Lets assume our architecture has a service layer that contains a transferFunds service. This service returns its result as response states. Here is how readable code that uses it is. This code could for example live in a controller for a REST API. (Examples are in CoffeeScript for readability)

services.transferFunds from: 'checking', to: 'savings', amount: 100, (result) ->
  result.is
    success: -> send status: 201, message: 'transfer complete'
    pending: -> send status: 202, message: 'transfer pending user approval'
    limit_exceeded: -> send status: 412, message: 'daily transaction limit exceeded'
    insufficient_funds: -> send status: 403, message: 'not enough funds'
    unknown_account: -> send status: 404, message: 'unknown account given'
    unauthorized: -> send status: 401, message: 'please log in first'
    other: -> send status: 500, message: 'please try again later'

In this example, the result is an instance of the Response class provided by this library. It has an is method, which calls the matching handler from the given hash of response state handlers. This represents the different code paths that deal with the different outcomes of the attempt to create a transfer in a very readable and maintainable way.

The special other response state acts as a catch-all, i.e. it is called if none of the states listed match the current condition. If no such catch-all handler is provided, unmatched errors cause an exception in the is method.

Implementing ResponseState APIs

With the help of this library, the API of the transferFunds service above can be implemented super easy like this:

Response = require 'response-state'

services.transferFunds = ({from, to, amount}, done) ->
  if not (fromAccount = getAccount from) then return done new Response('unknown_account')
  if from.balance < amount then return done new Response('insufficient_funds')
  if from.limit < amount then return done new Response('limit_exceeded')
  ...

  done null, new Response('transfer_finished', {confirmation, url})

Data handlers

The is method returns the result of the respective response state handler function. If the handler is not a function, it is returned directly. This allows to refactor the example above into:

services.transferFunds from: 'checking', to: 'savings', amount: 100, (result) ->
  [status, message] = result.is
    success: [201, 'transfer complete']
    pending: [202, 'transfer pending user approval']
    limit_exceeded: [412, 'daily transaction limit exceeded']
    insufficient_funds: [403, 'not enough funds']
    unknown_account: [404, 'unknown account given']
    unauthorized: [401, 'please log in first']
    other: [500, 'please try again later']
  send {status, message}

When to use

Response states are an optional pattern that should be used casually, i.e. only if the value it adds exceeds the complexity it introduces. This is probably mostly the case across larger functional boundaries, when calling into different architectural layers that perform complex operations. The responsibility of verifying the overall correctness of the code's behavior, i.e. that it transfers the right amounts between the right accounts at the right times, for the right reasons, remains with your test suite.

Development

See the developer documentation