Skip to content

Latest commit

 

History

History
285 lines (257 loc) · 6.66 KB

README.md

File metadata and controls

285 lines (257 loc) · 6.66 KB

version MIT License downloads

lessttp ⚡

Tiny tooling for headless HTTP-based lambda functions

Installation

npm install lessttp

Usage

Basic

Minimum viable async setup for Netlify Functions with built-in:

  • Content-Type=application/json when you return a JSON object in the body
  • 200 success status when there are no errors
  • Error handling
  • Parses deeply nested query string objects as JSON
  • Validates that GET HTTP method is used
const http = require('lessttp')
const StarWars = require('./services/StarWars')

exports.handler = http.function(async (request) => {
  const allJedi = await StarWars.queryJedi()
  return {
    body: allJedi,
  }
})

Custom error handling

const http = require('lessttp')
const StarWars = require('./services/StarWars')

exports.handler = http.function(async (request) => {
  try {
    const allJedi = await StarWars.queryJedi()
    return {
      body: allJedi,
    }
  } catch (error) {
    console.error(error)
    return {
      statusCode: 502,
      body: 'You were the chosen one!',
    }
  }
})

Custom path parameters

const http = require('lessttp')
const StarWars = require('./services/StarWars')
const baseUrl = '/.netlify/functions'

exports.handler = http.function({
  path: `${baseUrl}/jedi/:id`,
  async handler(request) {
    const { id } = request.params
    const jedi = await (id ? StarWars.getJediById(id) : StarWars.queryJedi())
    return {
      body: jedi,
    }
  },
})

Parsing deeply nested query objects

While the request query string is already parsed as JSON, the built-in middleware further uses qs.parse which supports deeply nested objects.

const http = require('lessttp')
const StarWars = require('./services/StarWars')
const baseUrl = '/.netlify/functions'

exports.handler = http.function({
  path: `${baseUrl}/jedi`,
  async handler(request) {
    const { id, filter: { name, side } } = request.query
    const jedi = await (id ? StarWars.getJediById(id) : StarWars.queryJedi({ name, side }))
    return {
      body: jedi,
    }
  },
})

Custom HTTP method

The request body is automatically parsed as JSON when available:

const http = require('lessttp')
const StarWars = require('./services/StarWars')

exports.handler = http.function({
  method: 'POST',
  async handler(request) {
    const jedi = await StarWars.createJedi(request.body)
    return {
      body: jedi,
    }
  }
})

Custom request validation

Validate request body, headers, params, or query using powerful JSON schemas:

const http = require('lessttp')
const StarWars = require('./services/StarWars')

exports.handler = http.function({
  method: 'POST',
  request: {
    body: {
      type: 'object',
      properties: {
        name: { type: 'string' },
        side: { type: 'string', enum: ['light', 'neutral', 'dark'] },
      },
      required: ['name', 'side'],
      additionalProperties: false,
    }
  },
  async handler(request) {
    const jedi = await StarWars.createJedi(request.body)
    return {
      body: jedi,
    }
  }
})

RESTful paths

Easily combine multiple handlers to create REST endpoints:

const http = require('lessttp')
const StarWars = require('./services/StarWars')

exports.handler = http.resource({
  async get() {
    const allJedi = await StarWars.queryJedi()
    return {
      body: allJedi,
    }
  },
  post: {
    request: {
      body: {
        type: 'object',
        properties: {
          name: { type: 'string' },
          side: { type: 'string', enum: ['light', 'neutral', 'dark'] },
        },
        required: ['name', 'side'],
        additionalProperties: false,
      }
    },
    async handler(request) {
      const jedi = await StarWars.createJedi(request.body)
      return {
        body: jedi,
      }
    },
  },
})

Override individual middleware functions

You can override individual functions by key:

const http = require('lessttp')
const StarWars = require('./services/StarWars')

exports.handler = http.function({
  middleware: {
    async parseBody(request) {
      if (request.body) {
        try {
          request.body = JSON.parse(request.body)
        } catch (error) {
          return {
            statusCode: 400,
            body: 'You were supposed to bring balance to the Force!'
          }
        }
      }
    }
  },
  async handler(request) {
    const jedi = await StarWars.createJedi(request.body)
    return {
      body: jedi,
    }
  }
})

Override all middleware

Replace the middleware array entirely:

const http = require('lessttp')
const { alias, validateHttpMethod } = require('lessttp/middleware')
const StarWars = require('./services/StarWars')

exports.handler = http.function({
  method: 'POST',
  middleware() {
    return [
      alias({ httpMethod: 'method', queryStringParameters: 'query' }),
      validateHttpMethod(this.method),
      async function parseBody(request) {
        if (request.body) {
          try {
            request.body = JSON.parse(request.body)
          } catch (error) {
            return {
              statusCode: 400,
              body: 'You were supposed to bring balance to the Force!'
            }
          }
        }
      }
    ]
  },
  async handler(request) {
    const jedi = await StarWars.createJedi(request.body)
    return {
      body: jedi,
    }
  }
})

Disable middleware

Disable the middleware completely and do everything yourself in the handler:

const http = require('lessttp')
const StarWars = require('./services/StarWars')

exports.handler = http.function({
  method: 'POST',
  middleware: null,
  async handler(request) {
    let body
    try {
      body = JSON.parse(request.body)
    } catch (error) {
      console.error(error)
      return {
        statusCode: 400,
        body: 'You were supposed to bring balance to the Force!'
      }
    }

    try {
      const jedi = await StarWars.createJedi(body)
      return {
        body: jedi,
      }
    } catch (error) {
      console.error(error)
      return {
        statusCode: 502,
        body: 'You were the chosen one!',
      }
    }
  }
})