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

billygoat

v1.0.0

Published

Lightweight, simple, client-side schema validation for databases like Google Cloud Firestore.

Downloads

19

Readme

billygoat

Lightweight, shallow, client-side schema validation. Billygoat helps keep track of documents created for non-relational databases, such as Google Cloud Firestore and Firebase.

Simple to use.

Installation

npm install --save billygoat

Documentation

Requests for clarificaiton can be made by opening an issue. Documentation will be updated frequently.

Usage

When creating a data model with a non-relational database, denormalizing your data is common. Keeping track of your documents and the names of each field can become complex and difficult. Using billygoat to enforce your schema can help reduce the complexity and help with the maintainability of your app.

If you are familiar with Mongoose, billygoat will be very easy to understand. There are two main differences. First, billygoat does not query the database. Second, billygoat has an optional step to validate your schema against a glossary.

Quick Start

You can use billygoat in three simple steps.

1. Create a new instance

var Billygoat = require('billygoat');
var goat = new Billygoat();

2. Define your document

goat.defineDocument({
    name: String,
    id: String,
    age: Number
});

3. Create your document

var firstGoat = goat.createDocument({
    name: "Gruff",
    id: "g1",
    age: 12
});

console.log(firstGoat); // returns {name: "Gruff", id: "g1", age: 12}

billygoat constructor examples

var goat1 = new billygoat();

var goat2 = new billygoat("goat");

var goat3 = new billygoat("goat", glossary);

var goat4 = new billygoat("goat", glossary, "rigid");

Parameters

When creating an instance of billygoat, three optional parameters can be passed in.

  • name - <String>
  • glossary - <Object>
  • 'rigid' - string 'rigid'

var goat = new billygoat(name, glossary, 'rigid');

  • All parameters are optional
  • Pass null to skip paramater

Name - first parameter

The first parameter name is a String that will be included if an error is thrown. Passing a string that matches the name of billygoat instance will help in locating the error.

For example, var firstGoat = new Billygoat('goat') 'goat' will refer to the variable name firstGoat from an error is thrown from one of its methods.

Glossary - second parameter

The second parameter glossary is a flat, JavaScript Object that represents your glossary. If you want to call it a dictionary or associative array that's fine.

The glossary is the record of all of your field names in your documents. As data is duplicated in your database (denormalized), it helps to keep a list of the field names you're using in your documents.

You can write a short, descriptive sentence for each value. It's better to make your glossary flat and readable and the descriptions succinct, as opposed to nested objects and wordy definitions.

For example

var glossary = {
    name: "name of document",
    age: "age of goat or troll",
    id: "id associated with the document"
}

NOTE: Billygoat does not check nested objects.

'rigid' - third parameter

The third paramter is the optional string 'rigid'.

If 'rigid' is passed in, billygoat will check if the document being created has exactly as many fields as the schema. If 'rigid' is omitted, it will only throw an error if the document being created has more fields than the schema.

For example, this will ALWAYS throw an error.

goat.defineDocument({
    name: String,
    id: String
});

var firstGoat = goat.createDocument({
    name: "Gruff",
    id: "g1",
    age: 12,
    message: "The next one is tastier."
})

This will SOMETIMES throw an error.

goat.defineDocument({
    name: String,
    id: String,
    age: Number,
    message: String,
    events: Array
});

var firstGoat = goat.createDocument({
    name: "Gruff",
    id: "g1"
})
  • The default behavior of billygoat is to let the second example pass.
  • Billygoat will only throw an error if the string 'rigid' is passed in as the third argument.
  • Using 'rigid' prevents newer documents from having more fields than older documents. Omitting 'rigid' allows for schemas to grow.

Methods

Billygoat has two methods, .defineDocument() and .createDocument(). Other methods will be exposed with later versisons.

.defineDocument()

The .defineDocument(<object>) method takes an object.

goat.defineDocument({
    name: String,
    age: Number,
    id: String
});

The .defineDocument() method defines the schema for the document. It throws an error if a glossary has been passed in AND one of the keys does not match to any of the keys in the glossary.

  • Billygoat does not check nested documents. This is intentional to encourage denormalization.

  • You can create separate schemas for nested documents. See the Example at the bottom.

.createDocument()

The .createDocument(<object>) method takes an object and returns the same object that was passed in, if it matches the schema. If it doesn't match the schema, it throws an error.

var firstGoat = goat.createDocument({
    name: "Gruff",
    age: 12,
    id: "g1"
})

Billygoat throws an error

  • if one or more of the fields are not in the glossary was passed in
  • if the value types do not match what was declared in .defineDocument()
  • if the number of fields differ according to 'rigid'

Data Types

The following datatypes are currently validated.

  • String
  • Number
  • Date - The string should be in a format recognized by JavaScript's Date.parse() method.
  • Boolean
  • Array
  • Object

Example

Define your glossary

First define your glossary.

Again, the glossary is a FLAT javascript object that contains key/value pairs of your data field names.

File Name glossary.js

'use strict';

var glossary = {

    name: "name of document",

    age: "age of goat or troll",

    id: "id associated with the document",

    latitude: "latitude of the event",

    longitude: "longitude of the event",

    mood: "Mood of troll",

    message: "Message delivered by either troll or goat",

    actors: "List of names"
}

module.exports = glossary;

Define your document schema

File Name billygoats.js

'use strict';

var Billygoat = require('billygoat');

var glossary = require('./glossary');

// create instances of billygoat
var goat = new Billygoat("goat", glossary);
var troll = new Billygoat("troll", glossary);
var bridge = new Billygoat("bridge", glossary);

// define your schemas
goat.defineDocument({
    name: String,
    age: Number,
    id: String,
    message: String,
    events: Array
});

troll.defineDocument({
    name: String,
    age: Number,
    id: String,
    mood: String,
    message: String,
    events: Array
});

bridge.defineDocument({
    name: String,
    longitude: Number,
    latitude: Number,
    id: String,
    actors: Array
});

// export your schemas
exports.goat = goat;
exports.troll = troll;
exports.bridge = bridge;

.createDocument()

Finally, create a document that matches the schema.

File Name index.js

'use strict';

var Story = require('./billygoats');

var firstGoat = Story.goat.createDocument({
    name: "Gruff",
    age: 9,
    id: "g1",
    message: "There's a tastier one coming after me.",
    events: ["bridge1"]
});

var secondGoat = Story.goat.createDocument({
    age: 15,
    name: "Gruff",
    id: "g2",
    message: "The next one is fatter.",
    events: ["bridge1"]
});

var thirdGoat = Story.goat.createDocument({
    name: "Gruff",
    age: 25,
    id: "g3",
    message: "Hasta la vista, baby.",
    events: ["bridge1"]
})

var troll = Story.troll.createDocument({
    name: "Mr. Troll",
    age: 438,
    id: "t1",
    message: "I'm hungry.",
    mood: "grumpy",
    events: ["bridge1"]
})

var stonyBridge = Story.bridge.createDocument({
    name: "Stony Bridge",
    id: "bridge1",
    latitude: 40.071881,
    longitude: -75.225580,
    actors: [firstGoat, secondGoat, thirdGoat, troll]
});

console.log(troll.message); // I'm hungry.
console.log(firstGoat.message); // There's a tastier one coming after me.
console.log(troll.message); // I'm hungry.
console.log(secondGoat.message); // The next one is fatter.
console.log(troll.message); // I'm hungry.
console.log(thirdGoat.message); // Hasta la vista, baby.

Example With Google Cloud Firestore

If you are using Google Cloud Firestore, you can update all of the documents to the database with a batched write.

// Get a new write batch
var batch = db.batch();

// Set the goat documents
var g1 = db.collection("actors").doc(firstGoat.id);
batch.set(g1, firstGoat);

var g2 = db.collection("actors").doc(secondGoat.id);
batch.set(g2, secondGoat);

var g3 = db.collection("actors").doc(secondGoat.id);
batch.set(g3, thirdgoat);

// Set the troll document
var t1 = db.collection("actors").doc(troll.id);
batch.set(t1, troll);

// Set the bridge document
var b1 = db.collection("events").doc(stonyBridge.id);
batch.set(b1, stonyBridge);

// Commit the batch
batch.commit().then(function () {
    // ...
});

Contributing

Contributors are welcome for bugs and features. Please submit a pull request.