3.2.4 • Published 4 months ago

@tsndr/cloudflare-worker-router v3.2.4

Weekly downloads
-
License
MIT
Repository
github
Last release
4 months ago

Cloudflare Workers Router

Cloudflare Workers Router is a super lightweight router (1.0K gzipped) with middleware support and ZERO dependencies for Cloudflare Workers.

When I was trying out Cloudflare Workers I almost immediately noticed how fast it was compared to other serverless offerings. So I wanted to build a full-fledged API to see how it performs doing real work, but since I wasn't able to find a router that suited my needs I created my own.

I worked a lot with Express.js in the past and really enjoyed their middleware approach, but since none of the available Cloudflare Worker routers offered middleware support at the time, I felt the need to create this router.

Contents

Features

  • ZERO dependencies
  • Lightweight (1.0K gzipped)
  • Fully written in TypeScript
  • Integrated Debug-Mode & CORS helper
  • Built specifically around Middlewares

Usage

Migrating from v2.x.x, check out the Migration Guide.

TypeScript Example

import { Router } from '@tsndr/cloudflare-worker-router'

// Env Types
export type Var<T = string> = T
export type Secret<T = string> = T

export type Env = {
    // Example binding to KV. Learn more at https://developers.cloudflare.com/workers/runtime-apis/kv/
    // MY_KV_NAMESPACE: KVNamespace
    //
    // Example binding to Durable Object. Learn more at https://developers.cloudflare.com/workers/runtime-apis/durable-objects/
    // MY_DURABLE_OBJECT: DurableObjectNamespace
    //
    // Example binding to R2. Learn more at https://developers.cloudflare.com/workers/runtime-apis/r2/
    // MY_BUCKET: R2Bucket

    ENVIRONMENT: Var<'dev' | 'prod'>

    SECRET_TOKEN: Secret
}

// Request Extension
export type ExtReq = {
    userId?: number
}

// Context Extension
export type ExtCtx = {
    //sentry?: Toucan
}

// Initialize Router
const router = new Router<Env, ExtCtx, ExtReq>()

// Enabling build in CORS support
router.cors()

// Register global middleware
router.use(({ env, req }) => {
    // Intercept if token doesn't match
    if (req.headers.get('authorization') !== env.SECRET_TOKEN)
        return new Response(null, { status: 401 })
})

// Simple get
router.get('/user', () => {
    return Response.json({
        id: 1,
        name: 'John Doe'
    })
})

// Post route with url parameter
router.post('/user/:id', ({ req }) => {

    const userId = req.params.id

    // Do stuff

    if (!true) {
        return Response.json({
            error: 'Error doing stuff!'
        }, { status: 400 })
    }

    return Response.json({ userId }, { status: 204 })
})

// Delete route using a middleware
router.delete('/user/:id', ({ env, req }) => {
    if (req.headers.get('authorization') === env.SECRET_TOKEN)
        return new Response(null, { status: 401 })

}, ({ req }) => {

  const userId = req.params.id

  // Do stuff...

  return Response.json({ userId })
})

// Listen Cloudflare Workers Fetch Event
export default {
    async fetch(request: Request, env: Env, ctx: ExecutionContext) {
        return router.handle(request, env, ctx)
    }
}
import { Router } from '@tsndr/cloudflare-worker-router'

// Initialize router
const router = new Router()

// Enabling build in CORS support
router.cors()

// Register global middleware
router.use(({ env, req }) => {
    // Intercept if token doesn't match
    if (req.headers.get('authorization') !== env.SECRET_TOKEN)
        return new Response(null, { status: 401 })
})

// Simple get
router.get('/user', () => {
    return Response.json({
        id: 1,
        name: 'John Doe'
    })
})

// Post route with url parameter
router.post('/user/:id', ({ req }) => {

    const userId = req.params.id

    // Do stuff

    if (!true) {
        return Response.json({
            error: 'Error doing stuff!'
        }, { status: 400 })
    }

    return Response.json({ userId }, { status: 204 })
})

// Delete route using a middleware
router.delete('/user/:id', ({ env, req }) => {
    if (req.headers.get('authorization') === env.SECRET_TOKEN)
        return new Response(null, { status: 401 })

}, ({ req }) => {

  const userId = req.params.id

  // Do stuff...

  return Response.json({ userId })
})

// Listen Cloudflare Workers Fetch Event
export default {
    async fetch(request, env, ctx) {
        return router.handle(request, env, ctx)
    }
}

Reference

router.debug([state = true])

Enable or disable debug mode. Which will return the error.stack in case of an exception instead of and empty 500 response. Debug mode is disabled by default.

state

State is a boolean which determines if debug mode should be enabled or not (default: true)

KeyTypeDefault Value
statebooleantrue

router.use([...handlers])

Register a global middleware handler.

handler(ctx)

Handler is a function which will be called for every request.

ctx

Object containing env, req

router.cors([config])

If enabled will overwrite other OPTIONS requests.

config (object, optional)

KeyTypeDefault Value
allowOriginstring*
allowMethodsstring*
allowHeadersstring*
allowCredentialsbooleanundefined
varystringundefined
maxAgeinteger86400
optionsSuccessStatusinteger204

Supported Methods

  • router.any(url, [...handlers])
  • router.delete(url, [...handlers])
  • router.get(url, [...handlers])
  • router.head(url, [...handlers])
  • router.options(url, [...handlers])
  • router.patch(url, [...handlers])
  • router.post(url, [...handlers])
  • router.put(url, [...handlers])

url (string)

The URL starting with a /. Supports the use of dynamic parameters, prefixed with a : (i.e. /user/:userId/edit) which will be available through the req-Object (i.e. req.params.userId).

handlers (function, optional)

An unlimited number of functions getting ctx passed into them.

ctx-Object

KeyTypeDescription
envobjectEnvironment
reqreq-ObjectRequest Object
ctxctx-ObjectCloudflare's ctx-Object

req-Object

KeyTypeDescription
bodyobject / stringOnly available if method is POST, PUT, PATCH or DELETE. Contains either the received body string or a parsed object if valid JSON was sent.
headersHeadersRequest Headers Object
methodstringHTTP request method
paramsobjectObject containing all parameters defined in the url string
queryobjectObject containing all query parameters

Getting started

Please follow Cloudflare's Get started guide to install wrangler.

Initialize Project

wrangler init <name>

Use of TypeScript is strongly encouraged :)

npm i -D @tsndr/cloudflare-worker-router

TypeScript (src/index.ts)

import { Router } from '@tsndr/cloudflare-worker-router'

// Env Types
export type Var<T = string> = T
export type Secret<T = string> = T

export type Env = {
  // Example binding to KV. Learn more at https://developers.cloudflare.com/workers/runtime-apis/kv/
  // MY_KV_NAMESPACE: KVNamespace
  //
  // Example binding to Durable Object. Learn more at https://developers.cloudflare.com/workers/runtime-apis/durable-objects/
  // MY_DURABLE_OBJECT: DurableObjectNamespace
  //
  // Example binding to R2. Learn more at https://developers.cloudflare.com/workers/runtime-apis/r2/
  // MY_BUCKET: R2Bucket
  //
  // Example Variable
  // ENVIRONMENT: Var<'dev' | 'prod'>
  //
  // Example Secret
  // JWT_SECRET: Secret
}

// Request Extension
export type ExtReq = {
  userId?: number
}

// Context Extension
export type ExtCtx = {
  //sentry?: Toucan
}

// Handler Type
export type Handler = RouterHandler<Env, ExtCtx, ExtReq>

// Initialize Router
const router = new Router<Env, ExtCtx, ExtReq>()

// Enable Debug Mode
router.debug()

// Enabling build in CORS support
//router.cors()

/// Example Route
//
// router.get('/hi', async () => {
//   return new Response('Hello World')
// })


/// Example Route for splitting into multiple files
//
// const helloHandler: Handler = async () => {
//    return new Response('Hello World')
// }
//
// router.get('/hellow', helloHandler)

// TODO: add your routes here

export default {
  async fetch(request: Request, env: Env, ctx: ExecutionContext) {
    return router.handle(request, env, ctx)
  }
}

JavaScript (src/index.js)

import { Router } from '@tsndr/cloudflare-worker-router'

const router = new Router()

// Enable Debug Mode
//router.debug()

// Enabling build in CORS support
//router.cors()

/// Example Route
//
// router.get('/hi', async () => {
//    return new Response('Hello World')
//})

/// Example Route for splitting into multiple files
//
// async function hiHandler() {
//    return new Response('Hello World')
// }
//
// router.get('/hi', hiHandler)

// TODO: add your routes here

export default {
    async fetch(request, env, ctx) {
        return router.handle(request, env, ctx)
    }
}
3.2.4

4 months ago

3.2.3

4 months ago

3.2.2

4 months ago

3.0.0

9 months ago

3.1.3

6 months ago

3.1.2

6 months ago

3.1.1

7 months ago

3.1.0

8 months ago

3.0.0-15

12 months ago

3.0.0-16

12 months ago

3.0.0-14

12 months ago

2.3.3

12 months ago

3.0.0-12

1 year ago

3.0.0-11

1 year ago

3.0.0-7

1 year ago

3.0.0-6

1 year ago

3.0.0-9

1 year ago

3.0.0-10

1 year ago

3.0.0-8

1 year ago

2.3.2

1 year ago

3.0.0-5

1 year ago

2.2.0

2 years ago

2.3.0

1 year ago

2.1.2

2 years ago

2.1.1

2 years ago

2.3.1

1 year ago

3.0.0-1

1 year ago

3.0.0-3

1 year ago

3.0.0-2

1 year ago

3.0.0-4

1 year ago

2.1.0

2 years ago

1.3.7

2 years ago

1.3.6

2 years ago

1.3.5

2 years ago

2.0.0-pre.1

2 years ago

2.0.0-pre.0

2 years ago

2.0.0-pre.5

2 years ago

2.0.2

2 years ago

2.0.0-pre.4

2 years ago

2.0.0-pre.3

2 years ago

2.0.0-pre.2

2 years ago

2.0.0-pre.9

2 years ago

1.3.10

2 years ago

2.0.0-pre.7

2 years ago

2.0.0-pre.6

2 years ago

2.0.0-pre.13

2 years ago

2.0.0-pre.12

2 years ago

2.0.0-pre.11

2 years ago

2.0.1

2 years ago

2.0.0-pre.10

2 years ago

2.0.0

2 years ago

1.3.8

2 years ago

1.3.4

2 years ago

1.3.3

2 years ago

1.3.2

2 years ago

1.2.4

2 years ago

1.3.1

2 years ago

1.3.0

2 years ago

1.2.0

2 years ago

1.2.3

2 years ago

1.2.2

2 years ago

1.2.1

2 years ago

1.1.9

2 years ago

1.1.8

2 years ago

1.1.11

2 years ago

1.1.7

3 years ago

1.1.6

3 years ago

1.1.5

3 years ago

1.1.1

3 years ago

1.1.0

3 years ago

1.1.3

3 years ago

1.1.2

3 years ago

1.0.4

3 years ago

1.0.3

3 years ago

1.0.2

3 years ago

1.0.1

3 years ago

1.0.0

3 years ago