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

restson

v1.0.3

Published

Simple REST API design and implementation, all in one JSON file, for Node

Downloads

4

Readme

Restson

Simple REST API design and implementation, all in one JSON file, for Node

Table of Contents

  1. Motivation
  2. Installation
  3. Quick start
    1. Sample project
  4. Writing endpoint handlers, middlewares
  5. API Json Schema
    1. Keys
    2. File path
  6. API
    1. Server
      1. options
    2. APIError
      1. APIError Constructor parameters
    3. ServerCodes

Motivation

After 3 years working with Node + Express to build Backend applications, I have faced a couple of problems related to REST APIs implementation:

  • Endpoints are declared in different files, which is very hard to track them and have an overview.
  • Some Devs have a habit to declare all endpoint handlers in one Js file. Which again make it hard to track when the amount of Endpoints increases.
  • Verbose code:
    • Always have to declare a router (express.Router()) when using a middleware, then use the router to use another sub route, then use the main router to use the sub router, ....
    • Always have to write export / require | import when using the Endpoint handlers, middlewares, ...
  • Ugly default error handler, if we wanna implement a beautiful error handler, all the Endpoint handler functions have to be in the try / catch block, and call next(err) at the end to pass the error to Error Handler middleware.

With all the reasons above, I decided to write this small library, with a hope to help my beloved fellow devs have an easier way to Design and implement the REST APIs for the Backend apps. The general principal is, we focus on writing the Endpoint handlers, middlewares, request validation schema only, each in one file. All the APIs can be written in one JSON file, no need to declare routers. This way enforce a cleaner architecture and all devs can have a much clearer overview on the whole API set.

Installation

> npm install restson

Quick start

Sample project

We wanna have a set of REST APIs:

GET  /api/v1/health-check
GET  /api/v1/orders/
POST /api/v1/orders/
GET  /api/v1/articles/
GET  /api/v1/articles/:id

Directory structure

├── controllers
│   ├── healthCheck.js
│   ├── orders
│   │   ├── getOrders.js
│   │   ├── createOrder.js
│   ├── articles
│   │   ├── getArticles.js
│   │   ├── getArticleById.js
│   │   ├── getArticleById.params.validation.json // Written in JSON Schema - https://json-schema.org/
│   ├── middlewares
│   │   ├── printDate.js
├── my.api.schema.json
├── server.js

In file healthCheck.js

module.exports = (req, res) => res.send('Ok!');

In file my.api.schema.v1.json

{
  "dir": "controllers",
  "/health-check": {
    "get": "healthCheck"
  },
  "/orders": {
    "dir": "orders",
    "get": "getOrders",
    "post": "createOrder"
  },
  "/articles": {
    "dir": "articles",
    "get": "getArticles",
    "/:id": {
      "get": "getArticleById",
      "validation": {
        "params": "getArticleById.params.validation"
      }
    }
  },
  "middlewares": [
    "middlewares/printDate"
  ]
}

In file server.js

const { Server } = require('restson');
const apiSchema = require('./my.api.schema.v1');

new Server({ apiSchema, rootUrl: '/api/v1' }).start();

Writing endpoint handlers, middlewares

This lib enforces writing each endpoint handler in one js file only, the handler function must be exported at the end. The same rule applies to middlewares as well.

module.exports = (req, res) => res.send('Hello from an endpoint handler!');
module.exports = (req, res, next) => {
  console.log('Hello from a middleware!!!');
  console.log(new Date().toISOString());
  next();
}

API Json Schema

Keys

The API Json Schema includes 5 different key types:

  • dir: string: The main directory where the Endpoint handlers, middlewares and validation schemas are placed
  • middlewares: array<string>: A list of middlewares' file paths, each middleware is declared in one file. The order of the list will be the executed order of the middlewares.
  • validation: object: Declare the validation schemas of the endpoint. The validation schema is defined using Json schema
    • headers: Validation schema's file path for the request's headers
    • body: Validation schema's file path for the request's body
    • params: Validation schema's file path for the request's parameters
    • query: Validation schema's file path for the request's query
  • get, post, put, delete: string | object: define the method of the Endpoint. The value can be a string (file path of the endpoint handler) or an object:
    • handler: string: file path of the endpoint handler
    • validation: object: Same format as the validation key above. This is the validation of this specific endpoint and method
    • middlewares: Same format as the middlewares key above. This is the middlewares of this specific endpoint and method
  • Key which start with /. (E.g. /orders): This key define the Endpoint of the url. The value of it can have the same keys as defined above or also the sub routes.

File path

the file path to the endpoint handler can be the relative path (depend on dir value):

{
  "dir": "controllers",
  "/orders": {
    "dir": "orders",
    "get": {
      "hander": "getOrders" // The handler is placed at ./controllers/orders/getOrders.js
    }
  },
  "middlewares": [
    "middlewares/printDate" // Middleware is located at ./controllers/middlewares/printDate.js
  ]
}

or absolute path (independent on dir value), in this case, the value must start with ./

{
  "dir": "controllers",
  "/orders": {
    "dir": "orders",
    "get": {
      "handler": "getOrders"
    },
    "middlewares": [
      "./middlewares/checkOrder" // Middleware is located at ./middlewares/checkOrder.js
    ]
  }
}

API

Server

Server is the main component in restson:

const restson = require('restson');
const server = new restson.Server(options);
server.start();

options

| Property | Description | Type | Default | | ----------| -------------------------------------------- | ------- | ---------------- | | port | server will start at this port | Number | 3000 | | apiSchema | [Required] File path of the API JSON Schema | String | No default value | | rootUrl | Root URL of the API Set | String | / |

APIError

This is a predefined custom Error in Restson:

const { APIError, ServerCodes } = require('restson');

const getOrderController = (req, res) => {
  const order = OrderDAL.findById(req.params.orderId);

  if (order === null) {
    throw new APIError('Order not found!', ServerCodes.NOT_FOUND);
  }

  res.send(order);
};

module.export = getOrderController;

APIError Constructor parameters

throw new APIError(message, serverCode);

| Parameter | Description | Type | Default | | ----------- | -------------------------------|--------- | ----------------- | | message | Message to API Consumer Client | String | no default value | | serverCode | Server Code of the response | Number | 500 |

ServerCodes

This is a small util in Restson, it defines all server codes with easy-to-remember constant values:

module.exports = {
  CONTINUE: 100,
  SWITCHING_PROTOCOL: 101,
  PROCESSING: 102,
  EARLY_HINT: 103,
  OK: 200,
  CREATED: 201,
  ACCEPTED: 202,
  NON_AUTHORITATIVE_INFO: 203,
  NO_CONTENT: 204,
  RESET_CONTENT: 205,
  PARTIAL_CONTENT: 206,
  MULTI_STATUS: 207,
  ALREADY_REPORTED: 208,
  IM_USED: 226,
  MULTIPLE_CHOICE: 300,
  MOVED_PERMANENTLY: 301,
  FOUND: 302,
  SEE_OTHER: 303,
  NOT_MODIFIED: 304,
  USE_PROXY: 305,
  UNUSED: 306,
  TEMPORARY_REDIRECT: 307,
  PERMANENT_REDIRECT: 308,
  BAD_REQUEST: 400,
  UNAUTHORIZED: 401,
  PAYMENT_REQUIRED: 402,
  FORBIDDEN: 403,
  NOT_FOUND: 404,
  METHOD_NOT_ALLOW: 405,
  NOT_ACCEPTABLE: 406,
  PROXY_AUTHENTICATION_REQUIRED: 407,
  REQUEST_TIMEOUT: 408,
  CONFLICT: 409,
  GONE: 410,
  LENGTH_REQUIRED: 411,
  PRECONDITION_FAILED: 412,
  PAYLOAD_TOO_LARGE: 413,
  URI_TOO_LONG: 414,
  UNSUPPPORTED_MEDIA_TYPE: 415,
  RANGE_NOT_SATISFIABLE: 416,
  EXPECTATION_FAILED: 417,
  IM_A_TEAPOT: 418,
  MISDIRECTED_REQUEST: 421,
  UNPROCESSABLE_ENTITY: 422,
  LOCKED: 423,
  FAILED_DEPENDENCY: 424,
  TOO_EARLY: 425,
  UPGRADE_REQUIRED: 426,
  PRECONDITION_REQUIRED: 428,
  TOO_MANY_REQUESTS: 429,
  REQUEST_HEADER_FIELDS_TOO_LARGE: 431,
  UNAVAILABLE_FOR_LEGAL_REASONS: 451,
  INTERNAL_SERVER_ERROR: 500,
  NOT_IMPLEMENTED: 501,
  BAD_GATEWAY: 502,
  SERVICE_UNAVAILABLE: 503,
  GATEWAY_TIMEOUT: 504,
  HTTP_VERSION_NOT_SUPPORTED: 505,
  VARIANT_ALSO_NEGOTIATES: 506,
  INSUFFICIENT_STORAGE: 507,
  LOOP_DETECTED: 508,
  NOT_EXTENDED: 510,
  NETWORK_AUTHENTICATION_REQUIRED: 511,
};