# Quick start

## Quick start

[Install](https://stampit.js.org/essentials/installation) the JavaScript module from the NPM registry.

```bash
npm i @stamp/it
# or
npm i stampit
```

Create an empty stamp.

```javascript
const stampit = require('@stamp/it')

let Stamp = stampit() // creates new stamp
```

[Compose](https://stampit.js.org/essentials/what-is-a-stamp#composing-stamps) it with another stamp.

```javascript
const HasLog = require('./HasLog')

Stamp = Stamp.compose(HasLog) // creates a new stamp composed from the two
```

Add default [properties](https://stampit.js.org/api/properties) to it.

```javascript
Stamp = Stamp.props({ // creates a new derived stamp
  S3: require('aws-sdk').S3
})
```

Add an [initializer](https://stampit.js.org/api/initializers) (aka constructor).

```javascript
Stamp = Stamp.init(function ({ bucket }, { stamp }) { // creates a new derived stamp
  this.s3instance = new this.S3({ 
    apiVersion: '2006-03-01', 
    params: { Bucket: bucket || stamp.compose.configuration.bucket } // using configuration.bucket, see below
  })
})
```

Add a [method](https://stampit.js.org/api/methods).

```javascript
Stamp = Stamp.methods({ // creates a new derived stamp
  upload({ fileName, stream }) {
    this.log.info({ fileName }, 'Uploading file') // .log property from the HasLog stamp

    return this.s3instance.upload({ Key: fileName, Body: stream }).promise() // see above
  }
})
```

Add a [configuration](https://stampit.js.org/api/configuration).

```javascript
Stamp = Stamp.conf({ // creates a new derived stamp
  bucket: process.env.UPLOAD_BUCKET
})
```

Add a [static](https://stampit.js.org/api/static-properties) method.

```javascript
Stamp = Stamp.statics({ // creates a new derived stamp
  setDefaultBucket(bucket) {
    return this.conf({ bucket })
  }
})
```

Make the `.s3instance` , `.log`, and `.S3` properties [private](https://stampit.js.org/ecosystem/stamp-privatize).

```javascript
const Privatize = require('@stamp/privatize')

Stamp = Stamp.compose(Privatize) // creates a new derived stamp
```

Give it a proper name.

```javascript
const FileStore = Stamp.compose({ name: 'FileStore' })
```

Create objects from your stamp.

```javascript
const store = FileStore()
```

### Shorter API

Here is the same `FileStore` stamp but written in a more concise way.

{% code title="S3FileStore.js" %}

```javascript
const HasLog = require('./HasLog')
const Privatize = require('@stamp/privatize')

const FileStore = HasLog.compose(Privatize, {
  name: 'FileStore',
  props: {
    S3: require('aws-sdk').S3
  },
  init({ bucket }, { stamp }) {
    this.s3instance = new this.S3({ 
      apiVersion: '2006-03-01', 
      params: { Bucket: bucket || stamp.compose.configuration.bucket }
    })
  },
  methods: {
    upload({ fileName, stream }) {
      this.log.info({ fileName }, 'Uploading file')
      return this.s3instance.upload({ Key: fileName, Body: stream }).promise()
    }
  },
  conf: {
    bucket: process.env.UPLOAD_BUCKET
  },
  statics: {
    setDefaultBucket(bucket) {
      return this.conf({ bucket })
    }
  }
})
```

{% endcode %}

The reusable `HasLog` stamp can be implemented in the following way.

{% code title="HasLog.js" %}

```javascript
module.exports = require('@stamp/it')({
  init(_, { stamp }) {
    // this will reuse the stamp name "FileStore" we set above
    this.log = require('bunyan').createLogger({ name: stamp.name })
  }
})
```

{% endcode %}

### Using the stamp

Use your stamp ad-hoc.

```javascript
async function uploadTo(req, res) {
  const fileStore = FileStore({ bucket: req.params.bucket }) // create instance

  await fileStore.upload({ fileName: req.query.file, stream: req }) // use the method of the stamp

  res.sendStatus(201)
}
```

Or preconfigure it.

```javascript
const CatGifStore = FileStore.setDefaultBucket('cat-gifs') // pre-configuring the bucket name

const catGifStore = CatGifStore() // create an instance of the store

await catGifStore.upload({ fileName: 'cat.gif', stream: readableStream })
```

If you want to silence the shameful fact that you are collecting cat gifs then here is how you disable log. Just overwrite the `log` property with a silent one.

```javascript
const SilentCatGifStore = CatGifStore.props({
  log: {
    info() {} // silence!
  }
})

await SilentCatGifStore().upload({ fileName: 'cat.gif', stream: readableStream })
```

Alternatively, you can have a generic silent logger stamp. Compose with it to override the `HasLog`.

```javascript
const HasSilentLog = stampit.props({ log: { info: () => {} } })

const SilentCatGifStore = CatGifStore.compose(HasSilentLog)
```

The `new` keyword also works with any stamp.

```javascript
const catGifStore = new CatGifStore()
```

Another way to create an object is to use `.create()` static method.

```javascript
const catGifStore = CatGifStore.create()
```

## Mocking I/O in unit tests

Replacing actual `S3` with a fake one.

```javascript
const MockedFileStore = FileStore.props({
  S3() {
    return {
      upload() {
        return { promise() { return Promise.resolve() } }
      }
  }
})

// Same as above but a one-liner:

const MockedFileStore = FileStore.props({
  S3: () => ({ upload: () => ({ async promise() {} }) })
})
```

## Basic API

Head to the [Basics](https://stampit.js.org/api/basics) page to view more API documentation. However, by now you already know all you need.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://stampit.js.org/api/quick-start.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
