Skip to content

matt-filion/s3-db

Repository files navigation

Node.js CI

Document DB API for AWS S3

AWS S3 is incredibly cheap, has 2 9's of availability, 12 9s of resiliency, triggers via AWS Lambda, cross region replication, versioning and pretty decent Performance. Its a pretty compelling database solution for a lot of scenarios. A few other people agree, see Pete Warden's blog and this interesting solution.

Whats New

This library has been changed drastically from the 1.x.x version as this was written to be TypeScript first. Also:

  • It makes use of async/await patterns.
  • TypeScript decorators signficantly improved/reduced configuration.
  • Model classes are no longer decorated with convenience functions (just metadata.)
  • A new collection instance is no longer async/promisified.
  • No need to create a 'Database' object.

Next?

  • JavaScript Examples
  • eTag verification on save, for collission detection.
  • Carry over 'copy'
  • Iterator pattern on ResourceList, or instead of.
  • Performance docs on Lambda @ 128mb, 512mb, 1024mb. 2024mb, 3036mb.
  • Bug: MD5 does not appear to be persisting or returning when doing .head() check on a document.

Usage

Installation

npm install s3-db --save into the project where S3-DB will be used. If you dont already have the aws-sdk node module then npm install aws-sdk as well.

Configure S3DB

There are very reaonsable out of the box configurations, but if you would like to change any of them you can do so using the S3DB class. The only values that you need to worry about at the global level are the following.

Here is a basic example overwriting all default values.

S3DB.update({
    baseName: 'myapp',
    stage: 'quality',
    region: 'us-east-2',
    bucketPattern: '{{stage}}-{{region}}-{{baseName}}-{{bucketName}}'
});

Note: Below in the configuration section each of these values is explained in detail.

Decorate Model/Types

Create your model class and decorate it with @collection() and @id() appropriately. So that when an instance of that class type is passed into the appropriate Collection instance, it will know how it should be configured.

If you do not specify an argument for @collection() then the class type will be lower cased, and used as the name and all collection configuration defaults used.

Here is a very basic example where you are fine with the name being generated by s3-db.

@collection()
export class User {

    @id()
    private id?: string;
    private name?: string;
    private age?: name;
    private address?: Address;

    constructor(){
    }
}

So once you have your model decorated, you can create an instance of a Collection and begin creating/updating/deleting objects in a Bucket. Function on Collection is async/promisified so you can use either pattern.

Async Example.

const collection: Collection<User> = new Collection(User);
function async doStuff(){
    const user: User = await collection.save({name:'Testing',age:21});
    const checkedUser: User = await collection.load(user.id);
    await collection.delete(user.id);
}

Promise Example.

const collection: Collection<User> = new Collection(User);

/* Creates a user and generates an ID for the user record. */
collection.save({name:'Testing',age:21})
    .then( (user: User) => collection.load(user.id) )
    .then( (user: User) => collection.delete(user.id) );

Configuration

A complete list of all the configuration points and what values you can use.

S3DB

Configurations that are applied across all collections.

Name Default Description
baseName s3db Used in the bucketPattern and logging as a namespace.
stage dev The logical environment. Used in the bucketPattern.
region us-west-2 Used in the AWS configuration to target a specific region. Also used in the bucketpattern.
bucketPattern {{stage}}-{{region}}-{{baseName}}-{{bucketName}} The name that is used to lookup the bucket for a collection. Must use valid S3 bucket name characters. The replacement values for {{stage}}, {{region}}, {{baseName}} and {{bucketName}} are all case sensitive. You can omit any of them.

Collection

Configurations specific to a collection.

Name Default Description
pageSize 100 Maximum of 1000. How many documents to return when doing a .find() operation.
serversideEncryption true If S3 server side encryption is enabled (encryption at rest.)
checkIsMOdified true If enabled, save() operations will check if the object provided has been modified before being saved. If it is not modified it returns without attempting to save to S3.
isModified MD5IsModified A function that is used to check if an object is modified. If you override it, implement the IsModified interface.
serialization JSONSerialization How objects are serialized to a string before they are perstisted to S3.
defaultIdGenerator defaultIDGenerator Default generation is UUID v4. This is called when no generator is provided on the @id() annotation.
validator undefined A function that can be used to check if the object being saved is valid.
noExceptionOnNotFound false Changes the behavior to return undefined rather than throw an excpetion, when no document is found.

API's

The available objects, decorators and functions.

@collection(string? | CollectionConfiguration?)

Annotation indicates that a specific class corresponds to an S3 Bucket.

@id(generator?)

Anotation indicates what attribute or field on a class will be the key for the persisted object. If this annotation is not used then 'id' is used, or added.

S3DB

Singleton containing the 'global' configurations for the S3DB instance.

S3DB.update({ baseName?: string; stage?: string; bucketPattern?: string; region?: string })

Updates the default configuration with the values provided.

S3DB.setLogLevel(level: LogLevel): void

Updates the logging level of the S3DB logger and will change the default level that each collection instance defines.

Collection

Used to do CRUD operations. Need to create an instance to use.

const collection: Collection = new Collection(SomeClass);

Creates a new instance that will use the SomeClass definition (which should contain the @collection and @id decorators) to determine its configuration.

collection.head(id): Promise

Returns the metadata for the corresponding object identified by the id.

collection.exists(id): Promise

Returns true if the object exists within the corresponding S3 bucket.

collection.save(toSave): Promise

Creates or updates an object in S3 that corresponds to the id of the toSave object passed in. If there is no id on the object, then one is generated.

collection.delete(id): Promise

Removes an object from an S3 bucket for the corresponding id.

collection.find(prefix, pageSize, continuationToken): Promise

Returns a list of S3Metadata objects for all objects in the corresponding S3 bucket htat start with the prefix value provided. If continuationToken is passed in then the list will be a 'continuation' of a previous find operation.

collection.subCollection(prefix: string, typeOf: SomeClass): Collection

Creates a new collection where all operations will execute with the prefix in front of the id's used. So if the prefix is /users/ then when .load('1234') is called the request will result in an ID lookup for /users/1234. Similarly, all objects saved will have the prefix applied when the ID is generated by the save operation, or, when an ID is provided and it does not startWith() the configured prefix.

collection.setLogLevel(level: LogLevel): void

Lets you change the logging level for this specific collection instance. At creation of the collection, the logging level is taken from the S3DB logger, as a child logger is created from it.

Releases

No releases published

Packages

No packages published