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

@begin/validator

v1.0.3

Published

Validate request bodies of JSON or x-url-form-encoded content-type

Downloads

127

Readme

@begin/validator

NPM version GitHub CI status

Validate request bodies against a provided JSON Schema. Content-type's supported included application/json and application/x-www-form-urlencoded. JSON request bodies are validated directly against the schema while form encoded bodies are coerced into schema format.

Contributing & bugs

Please fork the repository, make the changes in your fork and include tests. Once you're done making changes, send in a pull request.

Bug reports

Please include a test which shows why the code fails.

Installation

npm i @begin/validator

Usage

import arc from '@architect/functions'
import validator from '@begin/validator'

const Book = {
    id: 'Book',
    type: 'object',
    properties: {
        title: { type: 'string' },
        author: { type: 'string' },
        publication_date: { type: 'integer' },
        ID: { type: 'string' }
    }
}

export const handler = arc.http.async(validate)

async function validate(request) {
    let res = validator(request, Book)
    if (!res.valid) {
        return {
            statusCode: 500,
            json: { error: res.errors.map(e => e.stack).join('\n') }
        }
    }
    // Data is valid!
}

Normalization of form data into JavaScript Objects

Data is passed from forms as a list of key value pairs of strings. When submitted to the server or passed to client side JavaScript these key value pairs must be converted into an Object. This package will normalize data by:

  • Converting key/value pairs into nested objects (i.e. 'user.addr[0].name' -> {user:addr:[{name:'Jane'}]})
  • Changing string values into integer or float based on Schema.
  • Changing boolean values into booleans based on Schema

Nested objects and arrays form keys

  • . (dots) expand into objects (i.e. 'user.addr' -> {user:addr:'value'})
  • [1] Bracket notation converts to arrays with order. Missing indexes are squashed.
  • [] Empty brackets convert to arrays, but can only be used at the end of keys. (i.e. 'foo[]', not 'bar[].something')

Normalization of types

Forms return all values as strings. Including boolean and number values. In addition false booleans (i.e. radio button not checked) will not be included in the submitted form data. If a schema is used these types (and missing booleans) are coerced into the expected types.

  const formValues = {
    'anInteger': '3',
    'aFloat': '3.1',
    'aBooleanTrue': 'on'
    // 'aRadioFalse':'' // false boolean won't show up in form output
  }

  const Schema = {
    'id': 'ComplexSchema',
    'type': 'object',
    'properties': {
      'aFloat': { 'type': 'number' },
      'anInteger': { 'type': 'integer' },
      'aBooleanTrue': { 'type': 'boolean' },
      'aBooleanFalse': { 'type': 'boolean' },
    }
  }

  console.log(formEncodingToSchema(formValues, Schema))
  // { aBooleanTrue: true, aBooleanFalse: false, anInteger: 3, aFloat: 3.1 }

This normalization works for objects submitted to the server and FormData objects on the client. Architect (and many other servers) will automatically parse the form encoded string on the server into a flat object. If there are any duplicated key/values they are passed into an array.

<input name=bar value=one/>
<input name=bar value=two/>
<input name=bar value=three/>
// Shows up as an array
export default post(req){
  console.log(req.body)
  // ['one','two','three']
}

On the client using FormData these duplicated keys are put into an iterable object without forming array.

<form>
  <input name=bar value=one/>
  <input name=bar value=two/>
  <input name=bar value=three/>
</form>
<script>
  const form = document.querySelector('form')
  const fromData = new FormData(form)
  console.log(Object.entries(formData))
  // {bar:"three"}
  console.log(formData.getAll('bar'))
  // ['one','two','three']
</script>

Converting this object into entries will only get one of the duplicate keys. Using the getAll method will get the array.

Duplicate Keys

Duplicate key names can create ambiguity parsing the output. Bracket notation bar[] is recommended to disambiguate the duplicates when using for clientside FormData objects. Alternatively you can declare the key as duplicated in the configuration (i.e. convertToNestedObject(formData,{duplicateKeys:['bar']})).

The following is an example with a complex object.

  const formValuesServer = {
    'foo[0]': 'one',
    'foo[1]': 'two',
    'foo[2]': 'three',
    'zoo[]': [ 'one', 'two', 'three' ],
    // repeated keys create an array
    'bar': [ 'one', 'two', 'three' ],
    // missing indexes
    'baz[0]': 'one',
    'baz[3]': 'three',
    'baz[4]': 'four',
    'user.addr[0].firstname': 'john',
    'user.addr[0].lastname': 'smith',
    'user.addr[1].firstname': 'jane',
    'user.addr[1].lastname': 'doe',
    'user.thing[0][0].person': 'something',
    'anInteger': '3',
    'aFloat': '3.1',
    'aBooleanTrue': 'on'
    // 'aRadioFalse':'' // false boolean won't show up in form output
  }

  const formData = new FormData()
  formData.append( 'foo[0]', 'one')
  formData.append( 'foo[1]', 'two')
  formData.append( 'foo[2]', 'three')
  formData.append( 'zoo[]', 'one')
  formData.append( 'zoo[]', 'two')
  formData.append( 'zoo[]', 'three')
  formData.append( 'bar', 'one')
  formData.append( 'bar', 'two')
  formData.append( 'bar', 'three')
  formData.append( 'baz[0]', 'one')
  formData.append( 'baz[3]', 'three')
  formData.append( 'baz[4]', 'four')
  formData.append( 'user.addr[0].firstname', 'john')
  formData.append( 'user.addr[0].lastname', 'smith')
  formData.append( 'user.addr[1].firstname', 'jane')
  formData.append( 'user.addr[1].lastname', 'doe')
  formData.append( 'user.thing[0][0].person', 'something')
  formData.append( 'anInteger', '3')
  formData.append( 'aFloat', '3.1')
  formData.append( 'aBooleanTrue', 'on')

  const ComplexSchema = {
    'id': 'ComplexSchema',
    'type': 'object',
    'properties': {
      'aFloat': { 'type': 'number' },
      'anInteger': { 'type': 'integer' },
      'aBooleanTrue': { 'type': 'boolean' },
      'aBooleanFalse': { 'type': 'boolean' },
    }
  }

  const javascriptObject = {
    foo: [ 'one', 'two', 'three' ],
    zoo: [ 'one', 'two', 'three' ],
    bar: [ 'one', 'two', 'three' ],
    baz: [ 'one', 'three', 'four' ],
    user: {
      addr: [
        { firstname: 'john', lastname: 'smith' },
        { firstname: 'jane', lastname: 'doe' }
      ],
      thing: [
        [ { person: 'something' } ]
      ]
    },
    aBooleanTrue: true,
    aBooleanFalse: false,
    anInteger: 3,
    aFloat: 3.1,
  }
  const resultFromObject = formEncodingToSchema(convertToNestedObject(formValues), ComplexSchema)
  const resultFromIterable = formEncodingToSchema(convertToNestedObject(formData, { duplicateKeys: [ 'bar' ] }), ComplexSchema)