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
An assortment of delicious (yet lightweight and tree-shakeable) extras for the calorie-light itty-router.
Installation
npm install itty-router itty-router-extrasIncludes 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/catchblocks within your middleware/handlers, or manually calling a.catch(error)on therouter.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 a router (this is just an error-safe wrapper around itty router)
const router = ThrowableRouter({ base: '/todos' })
// optional shortcut to avoid per-route calls
// 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('/todos', withContent, ({ content }) => {
  return 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 =>
  // router.handle expects a Request as the first param, then anything else gets passed along!
  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 -->
204text(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??
