JSPM

  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 4550
  • Score
    100M100P100Q134462F
  • License MIT

An assortment of delicious extras for the calorie-light itty-router.

Package Exports

  • itty-router-extras

This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (itty-router-extras) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.

Readme

Itty Router

npm package Build Status Coverage Status Open Issues

An assortment of delicious (yet lightweight and tree-shakeable) extras for the calorie-light itty-router.

Installation

npm install itty-router itty-router-extras

Includes the following:

class

  • StatusError - throw these to control HTTP status codes that itty responds with.

middleware (add inline as route handlers)

  • withContent - safely parses and embeds content request bodies (e.g. text/json) as request.content
  • withCookies - embeds cookies into request as request.cookies (object)
  • withParams - embeds route params directly into request as a convenience

response

  • error - returns JSON-formatted Response with { error: message, status } and the matching status code on the response.
  • json - returns JSON-formatted Response with options passed to the Response (e.g. headers, status, etc)
  • status - returns JSON-formatted Response with { message, status } and the matching status code on the response.
  • text - returns plaintext-formatted Response with options passed to the Response (e.g. headers, status, etc). This is simply a normal Response, but included for code-consistency with json()

routers

  • ThrowableRouter - this is a convenience wrapper around itty-router that simply adds automatic exception handling, rather than requiring try/catch blocks within your middleware/handlers, or manually calling a .catch(error) on the router.handle. Itty core is fantastic (biased review), but let's face it - first unhandled exception and BOOM - your Worker explodes. This prevents that from happening! Personally, this one is an absolute must for my projects to cut down on boilerplate code AND random CF explosions.

Example

import {
  json,
  missing,
  error,
  withContent,
  withParams,
  ThrowableRouter,
} from 'itty-router-extras'

const todos = [
  { id: '13', value: 'foo' },
  { id: '14', value: 'bar' },
  { id: '15', value: 'baz' },
]

// create an error-safe itty router
const router = ThrowableRouter({ base: '/todos' })

// optional [safe] shortcut to avoid per-route calls below
// router.all('*', withParams, withContent)

// GET collection index
router.get('/', () => asJSON(todos))

// GET item - only return if found
router.get('/:id', withParams, ({ id }) => {
  const todo = todos.find(t => t.id === id)

  if (todo) {
    return json(todo)
  }
})

// POST to the collection
router.post('/', withContent, ({ content }) =>
  content
  ? json({
      created: 'todo',
      value: content,
    })
  : error(400, 'You probably need some content for that...')
)

// 404 for everything else
router.all('*', () => missing('Are you sure about that?'))

// attach the router "handle" to the event handler
addEventListener('fetch', event =>
  event.respondWith(router.handle(event.request))
)

API

Classes

StatusError(status: number, message: string): Error
import { ThrowableRouter, StatusError } from 'itty-router-extras'

router.get('/bad', () => {
  throw new StatusError(400, 'Bad Request')
})

// GET /bad
400 {
  error: 'Bad Request',
  status: 400
}

Middleware

withContent: function
import { ThrowableRouter, StatusError } from 'itty-router-extras'

const router = ThrowableRouter()

router
  .post('/form', withContent, ({ content }) => {
    // body content (json, text, or form) is parsed and ready to go, if found.
  })
  .post('/otherwise', async request => {
    try {
      const content = await request.json()

      // do something with the content
    } catch (err) {
      throw new StatusError(400, 'Bad Request')
    }
  })
withCookies: function
import { withCookies } from 'itty-router-extras'

router.get('/foo', withCookies, ({ cookies }) => {
  // cookies are parsed from the header into request.cookies
})
withParams: function
import { withParams } from 'itty-router-extras'

router
  .get('/:collection/:id?', withParams, ({ collection, id }) => {
    // route params are embedded into the request for convenience
  })
  .get('/otherwise/:collection/:id?', ({ params }) => {
    // this just saves having to extract params from the request.params object
    const { collection, id } = params
  })

Response

error(status: number, message?: string): Response
import { error, json } from 'itty-router-extras'

router.get('/secrets', request =>
  request.isLoggedIn
  ? json({ my: 'secrets' })
  : error(401, 'Not Authenticated')
)

// GET /secrets -->
401 {
  error: 'Not Authenticated',
  status: 401
}
json(content: object, options: object): Response
const todos = [
  { id: 1, text: 'foo' },
  { id: 2, text: 'bar' },
]

router.get('/todos', () => json(todos))
missing(message?: string): Response
router.get('/not-found', () => missing('Oops!  We could not find that page.'))

// GET /not-found -->
404 {
  error: 'Oops!  We could not find that page.',
  status: 404
}
status(status: number, message?: string): Response
router
  .post('/success', withContent, ({ content }) => status(204, 'Success!'))
  .post('/silent-success', withContent, ({ content }) => status(204))

// POST /success -->
204 {
  message: 'Success!',
  status: 204
}

// POST /silent-success -->
204
text(content: string, options?: object): Response
router.get('/plaintext', () => text('OK!'))

// GET /plaintext -->
200 OK!

Routers

ThrowableRouter(options?: object): Proxy

import { ThrowableRouter, StatusError } from 'itty-router-extras'

const router = ThrowableRouter()

router
  .get('/accidental', request => request.oops.this.doesnt.exist)
  .get('/intentional', request => {
    throw new StatusError(400, 'Bad Request')
  })

// GET /accidental
500 {
  error: 'Internal Error.',
  status: 500,
}

// GET /intentional
400 {
  error: 'Bad Request',
  status: 400,
}

Contributors

These folks are the real heroes, making open source the powerhouse that it is! Help out and get your name added to this list! <3

Core, Concepts, and Codebase

  • @mvasigh - for constantly discussing these ridiculously-in-the-weeds topics with me. And then for writing the TS interfaces (or simply re-writing in TS), right Mehdi??