2.4.2 • Published 2 years ago

hypr v2.4.2

Weekly downloads
3
License
MIT
Repository
github
Last release
2 years ago

hypr

npm version test coverage npm bundle size

Minimalist serverless API framework. Get started instantly with Presta.

npm i hypr

Hypr is little more than a thin wrapper around a normal AWS-lambda-flavored serverless function. That means you can use it on AWS, Netlify, or any other platform that supports the (Event, Context) => Response pattern. If you're using a framework like Presta, you can take this pattern to even more platforms, like Cloudflare and Vercel.

Usage

Hypr approaches middleware as a "stack". A request comes in, travels linearly through the stack, and is returned as a response on the other end.

A simple hypr-wrapped lambda that returns JSON might look like this:

import * as hypr from 'hypr'

export const handler = hypr.stack([
  hypr.main((ev, ctx) => {
    return {
      statusCode: 200,
      json: {
        auth: false,
      },
    }
  }),
])

Errors

Throw errors are handled automatically and serialized to JSON or HTML depending on the Accept header of the HTTP request. For more control, throw hypr HttpErrors, which allow you to specify status codes, messaging, and headers.

import * as hypr from 'hypr'

export const handler = hypr.stack([
  hypr.main((ev, ctx) => {
    if (!ctx.user) {
      throw new hypr.HttpError(403, `Please check your API token and try again.`)
    }

    return {
      statusCode: 200,
      json: {
        auth: false,
      },
    }
  }),
])

For more control, or to access errors for telemetry, provide an "error stack" to hypr.stack. In the event of a thrown exception, all error stack middleware will be run with the Error available on context.error.

export const handler = hypr.stack(
  [
    hypr.main((ev, ctx) => {
      if (!ctx.user) {
        throw new hypr.HttpError(403, `Please check your API token and try again.`)
      }

      // ...
    }),
  ],
  [
    (ev, ctx, res) => {
      console.error(ctx.error) // HttpError('Please check...')
    },
  ]
)

Middleware

Middleware can be added on either side of the main handler within the response stack. They can do everything from parse cookies to guard against unauthorized requests.

They look a little different from the main handler because they receive a third argument: the response. This allows middleware to transform or perform further processing on the response before it's sent back to the client.

import * as hypr from 'hypr'

const isAuthenticated = hypr.createMiddleware(async (ev, ctx, res) => {
  if (ev.cookies.session) {
    const user = await getLoggedInuser(ev.cookies.session)
    if (user) {
      ctx.user = user
    } else {
      throw new hypr.HttpError(403, `Session expired, please log in.`)
    }
  } else {
    throw new hypr.HttpError(403, `Please log in.`)
  }
})

export const handler = hypr.stack([
  isAuthenticated,
  hypr.main((ev, ctx) => {
    const { user } = ctx.user
    return { ... }
  })
])

Middleware can also return responses. If a response is returned from a middleware, the main handler will not be run. All other middleware will run as normal. This allows a stack to exit early, like in the case of a redirect.

const isAuthenticated = hypr.createMiddleware(async (ev, ctx, res) => {
  if (ev.cookies.session) {
    const user = await getLoggedInuser(ev.cookies.session)
    if (user) {
      ctx.user = user
    } else {
      return hypr.redirect(302, '/login')
    }
  } else {
    return hypr.redirect(302, '/login')
  }
})

API

stack

Required wrapper for all serverless functions utilizing hypr.

import { stack } from 'hypr'

export const handler = hypr.stack(
  [
    // ...response middleware
  ],
  [
    // ...error middleware
  ]
)

main

Required wrapper for the main handler of your stack.

import { stack, main } from 'hypr'

export const handler = hypr.stack([
  // ...response middleware
  main(...)
], [
  // ...error middleware
])

Main can be used one of two ways. General purpose:

main((ev, ctx) => { ... })

Or for specific HTTP methods. For example, to allow only POST (and CORS preflights):

main({
  post(ev, ctx) { ... }
})

Methods for get post put patch delete etc are all supported via <verb>(ev, ctx): HyprResponse pattern.

redirect

import { stack, redirect } from 'hypr'

export const handler = hypr.stack([
  (ev, ctx) => {
    return redirect(302, '/login')
  },
])

HttpError

Create a serializable error i.e. new HttpError(statusCode[, message, options]).

import { stack, HttpError } from 'hypr'

export const handler = hypr.stack([
  (ev, ctx) => {
    throw new HttpError(400)
  },
])

With all options:

export const handler = hypr.stack([
  (ev, ctx) => {
    throw new HttpError(400, 'An error occurred', {
      expose: true, // default false for >499 status codes
      headers: {
        'cache-control': 'max-age=0, private',
      },
    })
  },
])

headers

An incomplete list of common HTTP headers. Please contribute!

import { headers } from 'hypr'

console.log(headers.CacheControl) // cache-control

methods

A complete list of HTTP methods.

import { methods } from 'hypr'

console.log(methods.POST) // POST

mimes

An incomplete list of common mime types. Please contribute!

import { mimes } from 'hypr'

console.log(mimes.json) // application/json; charset=utf-8

Available middleware

Hypr ships with a couple of handy middleware.

Cookies

Parse and serialize cookies using sugarcookie. thaw and bake are just aliases for parse and serialize exports.

import { stack, main } from 'hypr'
import { thaw, bake } from 'hypr/cookies'

export const handler = stack([
  thaw(),
  main((event) => {
    const { session_id } = event.cookies
    const { id, expiresAt } = refreshSession(session_id)

    return {
      statusCode: 204,
      // create cookies via the response object
      cookies: {
        // shorthand, no options
        cookie_name: 'value',
        // with options
        session_id: [
          id,
          {
            expires: expiresAt,
            secure: true,
            httpOnly: true,
          },
        ],
      },
    }
  }),
  bake(),
])

Helmet

Protect your APIs with common security headers. Inspired by helmet.

import { stack, main } from 'hypr'
import { helmet } from 'hypr/helmet'

export const handler = stack([
  main(...),
  helmet(),
])

Creating middleware

Middleware run before and/or after any main handlers. Anything running before should attach props to event or context. Anything running after should read values from the response and merge in new values.

import { createHandler } from 'hypr'

export const set204IfNoBodyMiddleware = (options: any) => {
  return createMiddleware((event, context, response) => {
    if (!response.body) {
      response.statusCode = 204
    }
  })
}

License

MIT License © Sure Thing

2.5.0-beta.1

2 years ago

2.5.0-beta.2

2 years ago

2.5.0-beta.3

2 years ago

2.2.0

2 years ago

2.4.1

2 years ago

2.4.0

2 years ago

2.4.2

2 years ago

2.3.0

2 years ago

2.0.1

2 years ago

2.0.0

2 years ago

1.5.3

2 years ago

1.5.2

2 years ago

1.5.1

2 years ago

1.5.0

2 years ago

2.1.0

2 years ago

0.2.1

2 years ago

1.2.2

5 years ago

1.2.1

5 years ago

1.2.0

5 years ago

1.1.3

5 years ago

1.1.2

5 years ago

1.1.1

5 years ago

1.1.0

5 years ago

1.0.1

6 years ago

1.0.0

6 years ago

0.7.22

9 years ago

0.6.22

9 years ago

0.6.21

9 years ago

0.6.20

9 years ago

0.5.20

9 years ago

0.5.19

9 years ago

0.5.18

9 years ago

0.4.18

9 years ago

0.4.17

9 years ago

0.4.16

9 years ago

0.3.16

9 years ago

0.3.15

9 years ago

0.3.14

9 years ago

0.3.13

9 years ago

0.3.12

9 years ago

0.2.12

9 years ago

0.2.11

9 years ago

0.2.10

9 years ago

0.2.9

9 years ago

0.1.8

9 years ago

0.1.7

9 years ago

0.1.6

9 years ago

0.1.5

9 years ago

0.1.4

9 years ago

0.1.3

9 years ago

0.1.2

9 years ago

0.1.1

9 years ago

0.1.0

9 years ago

0.0.9

9 years ago

0.0.8

9 years ago

0.0.7

9 years ago

0.0.6

9 years ago

0.0.5

9 years ago

0.0.4

9 years ago

0.0.3

9 years ago

0.0.2

9 years ago