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

savage

v0.2.2

Published

Kinda like Mongoose, but for REST with Promises!

Downloads

3

Readme

Savage Build Status

Ooooohhh yeeah!

Kinda like Mongoose, but for REST with Promises!

About

Savage is intended to make it possible to write code like you do with Mongoose, but for REST.

I came up with the name "Savage" after finding that "restler" was already taken. I got to thinking though, "Who is an awesome wrestler?" and the first name that came to me was "Macho Man" Randy Savage. Also the name wasn't taken in NPM.

Sooo, here we are.

Intent

I found myself constantly writing controllers for REST calls using Request and Promises to do the heavy lifting with a bit of logic before and after the call to modify the actual HTTP request for things like authentication and validation.

Realizing that Mongoose had already addressed a lot of the issues that I was struggling with, just in the MongoDB space, I decided to write a module that would let me interact with REST operations in a similar fashion.

Concepts

For all of the examples, I'm going to refer to a http://localhost:3000 server that has the following basic REST CRUD routes/endpoints:

POST /users
GET /users
GET /users/{id}
PUT /users/{id}
DELETE /users/{id}

Client

The Client() is just a way to create a re-usable model for a server. Once you instantiate a Client(), you use that to create Endpoints.

Using our example server, you would create a Client like this:

let Client = require('savage');

let client = new Client('http://localhost:3000');

/*
let client = new Client({
  url: 'http://localhost:3000',
  middleware: [
    (options, resolve, reject) => {
      // do something for all client.Endpoint()s
      resolve(options);
    }
  ]
});
*/

With a client created, you can now add Endpoint models with which you will interact.

You can also add middleware to a client object and all subsequent Endpoint()s will have those middlewares included.

Endpoint

You can safely think of endpoints as the path parameters in a URL.

let Client = require('savage');

let client = new Client('http://localhost:3000');

let users = new client.Endpoint('/users');

/*
let users = new client.Endpoint({
  path: '/users',
  middleware: [
    (options, resolve, reject) => {
      // do something for all CRUD calls for this client.Endpoint()
      resolve(options);
    }
  ]
});
*/

Calling create(), read(), update(), or delete() on your endpoint will issue the corresponding HTTP request and will return a Promise that produces a simplified Request response object that lazily attempts to convert the body to JSON.

users.read() // GET http://localhost:3000/users
  .then((response) => {
    console.log(response.json); // outputs the response body as JSON
  })
users.read('abc123') // GET http://localhost:3000/users/abc123
  .then((response) => {
    console.log(response.json); // outputs the response body as JSON
  })

Middleware

The way that savage deals with middleware is pretty basic, but it gives a lot of flexibility to a call.

Middlewares can be added per Client(), per client.Endpoint() or per CRUD call and use the signature (options, resolve, reject).

When calling resolve() inside a middleware, you MUST provide the options object.

When calling reject() inside a middleware, you SHOULD provide an Error.

Client-level middleware

The getAccessToken() call would happen for every subsequent endpoint call made for this client.

Client-level middleware persist for all endpoints.

let Client = require('savage');

let client = new Client({
  url: 'http://localhost:3000',
  middleware: [
    (options, resolve, reject) => {
      getAccessToken((token) => {
        options.qs.access_token = token;
        // makes all HTTP calls have "?access_token={{token}}"
        resolve(options);
      })
    }
  ]
});

Endpoint-level

Endpoint-level middleware persist for all CRUD calls.

let Client = require('savage');

let client = new Client({
  url: 'http://localhost:3000'
});

let users = new client.Endpoint({
  path: '/users',
  middleware: [
    (options, resolve, reject) => {
      getUserId((userId) => {
        options.qs.userId = userId;
        // all HTTP calls to /users have "?userId={{userId}}"
        resolve(options);
      })
    }
  ]
});

Call-level

Call-level middlewares DO NOT persist and MUST come before the CRUD method.

let Client = require('savage');

let client = new Client({
  url: 'http://localhost:3000'
});

let users = new client.Endpoint({
  path: '/users'
});

users
  .use((options, resolve, reject) => {
    // do something to the request???
    options.headers.foo = 'bar';
    resolve(options);
  })
  .update('abc123', { username: 'alpha.bet' }) // PUT http://localhost:3000/users/abc123
  .then((response) => {
    console.log(response.json);
  })

Versions

  • 0.2.1 = Fix for when read() calls made w/o path args to prevent trailing slash
  • 0.2.0 = Client() and Endpoint() accept strings and objects
  • 0.1.0 = $ npm publish
  • 0.0.* = Internal development & testing