Skip to content

Commit

Permalink
Merge branch 'v4.x' into v4.x
Browse files Browse the repository at this point in the history
  • Loading branch information
kwhitley authored Nov 25, 2023
2 parents 7816ebd + a20651e commit a8575eb
Show file tree
Hide file tree
Showing 21 changed files with 1,456 additions and 1,401 deletions.
19 changes: 4 additions & 15 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -1,28 +1,17 @@
### Description

Please explain the changes you made here.

### Issue Related

- Link to the related issue:
### Related Issue
Link to the related issue:

### Type of Change (select one and follow subtasks)

- [ ] Documentation (README.md)
- [ ] Maintenance or repo-level work (e.g. linting, build, tests, refactoring, etc.)
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Is this a mainstream benefit or an edge case?
- [ ] Is it worth the bytes?
- [ ] I have included test coverage
- [ ] Breaking change (fix or feature that would cause existing functionality/userland code to not work as expected)
- [ ] Explain why a breaking change is necessary:
- [ ] This change requires (or is) a documentation update
- [ ] I have added necessary local documentation (if appropriate)
- [ ] I have added necessary [itty.dev](https://github.com/kwhitley/itty.dev) documentation (if appropriate)

### Testing

Please describe the tests that you ran to verify your changes.

### Checklist

- [ ] I have read the [CONTRIBUTING](../CONTRIBUTING.md) doc
21 changes: 21 additions & 0 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
name: lint

on:
push:
branches: [v4.x]
pull_request:
branches: [v4.x]

jobs:
build:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2
- name: Use Node.js
uses: actions/setup-node@v1
with:
node-version: '16.x'
- name: Install dependencies
run: yarn
- run: yarn lint
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -110,4 +110,7 @@ dist
docs/dist
docs/dist-ssr
docs/*.local
docs/pages/README.md
docs/pages/README.md

# JetBrains tools
.idea
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
## Changelog

- **v4.1.0**
- added: `HasContent<ContentType>` type to `withContent` (credit [@alexrosenfeld10](https://github.com/alexrosenfeld10))
- added: Adds basic text/formData support to supplement the native JSON support of `withContent`
- **v4.0.00** - Partial changelog below
- BREAKING: heavy TS rewrite for core Router (thank you, ChatGPT)
- added: nearly all extras from itty-router-extras
Expand Down
137 changes: 108 additions & 29 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
<p align="center">
<a href="https://itty.dev/itty-router">
<img src="https://github.com/kwhitley/itty-router/assets/865416/7751dac0-2dc8-4754-8b39-d08fc2144b4f" alt="Itty Router" />
<img src="https://github.com/kwhitley/itty-router/assets/865416/15a90b05-344d-4135-a3f8-52a4d117250e" alt="Itty Router" />
</a>
<p>

---

<h2 align="center"><a href="https://itty.dev/itty-router">v4.x Documentation @ itty.dev</a>
<br /></h2>

Expand Down Expand Up @@ -33,8 +32,8 @@
</p>

<p align="center">
<a href="https://discord.com/channels/832353585802903572" target="_blank">
<img src="https://img.shields.io/discord/832353585802903572?style=flat-square" alt="join us on discord" />
<a href="https://discord.gg/53vyrZAu9u" target="_blank">
<img src="https://img.shields.io/discord/832353585802903572?label=Discord&logo=Discord&style=flat-square&logoColor=fff" alt="join us on discord" />
</a>
<a href="https://github.com/kwhitley/itty-router" target="_blank">
<img src="https://img.shields.io/github/stars/kwhitley/itty-router?style=social" alt="repo stars" />
Expand All @@ -49,38 +48,29 @@

---

Itty aims to be the world's smallest (~440 bytes), feature-rich JavaScript router, enabling beautiful API code with a near-zero bundlesize. Designed originally for [Cloudflare Workers](https://itty.dev/itty-router/runtimes#Cloudflare%20Workers), itty can be used in browsers, Service Workers, edge functions, or standalone runtimes like [Node](https://itty.dev/itty-router/runtimes#Node), [Bun](https://itty.dev/itty-router/runtimes#Bun), etc.!
Itty is arguably the smallest (~450 bytes) feature-rich JavaScript router available, while enabling dead-simple API code.

Designed originally for [Cloudflare Workers](https://itty.dev/itty-router/runtimes#Cloudflare%20Workers), itty can be used in browsers, service workers, edge functions, or runtimes like [Node](https://itty.dev/itty-router/runtimes#Node), [Bun](https://itty.dev/itty-router/runtimes#Bun), etc.!

## Features:
## Features

- Absurdly tiny. The Router itself is ~440 bytes gzipped, and the **entire** library is under 1.6k!
- Absurdly easy to use. We believe route code should be self-evident, obvious, and read more like poetry than code.
- Absurdly agnostic. We leave **you** with full control over response types, matching order, upstream/downstream effects, etc.
- Works [anywhere, in any environment](https://itty.dev/itty-router/runtimes).
- [Fully typed/TypeScript support](https://itty.dev/itty-router/typescript), including hinting.
- Tiny. [~450](https://deno.bundlejs.com/?q=itty-router/Router) bytes for the Router itself, or [~1.6k](https://bundlephobia.com/package/itty-router) for the entire library (>100x smaller than [express.js](https://www.npmjs.com/package/express)).
- [Fully-Typed](https://itty.dev/itty-router/typescript).
- Shorter, simpler route code than most modern routers.
- Dead-simple [middleware](https://itty.dev/itty-router/middleware) - use ours or write your own.
- Supports [nested APIs](https://itty.dev/itty-router/nesting).
- Platform agnostic (based on [Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API)) - use it [anywhere, in any environment](https://itty.dev/itty-router/runtimes).
- Parses [route params](https://itty.dev/itty-router/route-patterns#params),
[optional params](https://itty.dev/itty-router/route-patterns#optional),
[wildcards](https://itty.dev/itty-router/route-patterns#wildcards),
[greedy params](https://itty.dev/itty-router/route-patterns#greedy),
and [file formats](https://itty.dev/itty-router/route-patterns#file-formats).
- Automatic [query parsing](https://itty.dev/itty-router/route-patterns#query).
- Easy [error handling](https://itty.dev/itty-router/errors), including throwing errors with HTTP status codes!
- Easy [Response](https://itty.dev/itty-router/responses) creation, with helpers for major formats (e.g.
[json](https://itty.dev/itty-router/api#json),
[html](https://itty.dev/itty-router/api#html),
[png](https://itty.dev/itty-router/api#png),
[jpeg](https://itty.dev/itty-router/api#jpeg), etc.)
- Deep APIs via [router nesting](https://itty.dev/itty-router/nesting).
- Full [middleware](https://itty.dev/itty-router/middleware) support. Includes the following by default:
- [withParams](https://itty.dev/itty-router/api#withParams) - access the params directly off the `Request` (instead of `request.params`).
- [withCookies](https://itty.dev/itty-router/api#withCookies) - access cookies in a convenient Object format.
- [withContent](https://itty.dev/itty-router/api#withContent) - auto-parse Request bodies as `request.content`.
- [CORS](https://itty.dev/itty-router/cors) - because we love you.
- Fully readable regex... yeah right! 😆
[file formats](https://itty.dev/itty-router/route-patterns#file-formats)
and [query strings](https://itty.dev/itty-router/route-patterns#query).
- Extremely extendable/flexible. We leave you in complete control.

## [Full Documentation](https://itty.dev/itty-router)

Complete documentation/API is available at [itty.dev](https://itty.dev/itty-router), or join our [Discord](https://discord.com/channels/832353585802903572) channel to chat with community members for quick help!
Complete API documentation is available at [itty.dev/itty-router](https://itty.dev/itty-router), or join our [Discord](https://discord.gg/53vyrZAu9u) channel to chat with community members for quick help!

## Installation

Expand Down Expand Up @@ -128,11 +118,100 @@ export default {
}
```

# What's different about itty? <a name="a-different-kind-of-router"></a>
Itty does a few things very differently from other routers. This allows itty route code to be shorter and more intuitive than most!

### 1. Simpler handler/middleware flow.
In itty, you simply return (anything) to exit the flow. If any handler ever returns a thing, that's what the `router.handle` returns. If it doesn't, it's considered middleware, and the next handler is called.

That's it!

```ts
// not middleware: any handler that returns (anything at all)
(request) => [1, 4, 5, 1]

// middleware: simply doesn't return
const withUser = (request) => {
request.user = 'Halsey'
}

// a middleware that *might* return
const onlyHalsey = (request) => {
if (request.user !== 'Halsey') {
return error(403, 'Only Halsey is allowed to see this!')
}
}

// uses middleware, then returns something
route.get('/secure', withUser, onlyHalsey,
({ user }) => `Hey, ${user} - welcome back!`
)
```

### 2. You don't have to build a response in each route handler.
We've been stuck in this pattern for over a decade. Almost every router still expects you to build and return a [Response](https://developer.mozilla.org/en-US/docs/Web/API/Response)... in every single route.

We think you should be able to do that once, at the end. In most modern APIs for instance, we're serving JSON in the majority of our routes. So why handle that more than once?
```ts
router
// we can still do it the manual way
.get('/traditional', (request) => json([1, 2, 3]))

// or defer to later
.get('/easy-mode', (request) => [1, 2, 3])

// later, when handling a request
router
.handle(request)
.then(json) // we can turn any non-Response into valid JSON.
```

### 3. It's all Promises.
We `await` every handler, looking for a return value. If we get one, we break the flow and return your value. If we don't, we continue processing handlers/routes until we do. This means that every handler can either be synchronous or async - it's all the same.

When paired with the fact that we can simply return raw data and transform it later, this is AWESOME for working with async APIs, database layers, etc. We don't need to transform anything at the route, we can simply return the Promise (to data) itself!

Check this out:
```ts
import { myDatabase } from './somewhere'

router
// assumes getItems() returns a Promise to some data
.get('/items', () => myDatabase.getItems())

// later, when handling a request
router
.handle(request)
.then(json) // we can turn any non-Response into valid JSON.
```

### 4. Only one required argument. The rest is up to you.
We only require one argument in itty - a Request-like object with the following shape: `{ url, method }` (usually a native [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request)). Because itty is not opinionated about [Response](https://developer.mozilla.org/en-US/docs/Web/API/Response) creation, there is not "response" argument built in. Every other argument you pass to `route.handle` is given to each handler, in the same order.

> ### This makes itty one of the most platform-agnostic routers, *period*, as it's able to match up to any platform's signature.
Here's an example using [Cloudflare Worker](https://workers.cloudflare.com/) arguments:
```ts
router
.get('/my-route', (request, environment, context) => {
// we can access anything here that was passed to `router.handle`.
})

// Cloudflare gives us 3 arguments: request, environment, and context.
// Passing them to `route.handle` gives every route handler (above) access to each.
export default {
fetch: (request, env, ctx) => router
.handle(request, env, ctx)
.then(json)
.catch(error)
}
```

## Join the Discussion!

Have a question? Suggestion? Complaint? Want to send a gift basket?

Join us on [Discord](https://discord.com/channels/832353585802903572)!
Join us on [Discord](https://discord.gg/53vyrZAu9u)!

## Testing and Contributing

Expand Down
17 changes: 12 additions & 5 deletions example/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { withParams } from 'itty-router'
import {
Router, // the router itself
IRequest, // lightweight/generic Request type
Expand All @@ -8,9 +9,10 @@ import {
error,
} from '../src'

// declare a custom Router type with used methods
interface CustomRouter extends RouterType {
puppy: Route
const myCustomMiddleware = (request: IRequest) => {
if (true) {
return false
}
}

// declare a custom Request type to allow request injection from middleware
Expand All @@ -28,14 +30,19 @@ const { corsify, preflight } = createCors()
const router = Router({ base: '/' })

router
.all('*', preflight)
.get<CustomRouter>('/authors', withAuthors, (request: RequestWithAuthors) => {
.all('*', preflight, withParams)
.get('/authors', withAuthors, (request: RequestWithAuthors) => {
return request.authors?.[0]
})
.puppy('/:name', (request) => {
const name = request.params.name
const foo = request.query.foo
})
.get('/whatever/:foo/:bar',
myCustomMiddleware,
myCustomMiddleware,
({ foo, bar }) => ({ foo, bar })
)
.all('*', () => error(404))

// CF ES6 module syntax
Expand Down
File renamed without changes.
20 changes: 20 additions & 0 deletions example/node-require.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
const { createServerAdapter } = require('@whatwg-node/server')
const { createServer } = require('http')
require('isomorphic-fetch')
const { Router, error, json } = require('../dist/index.js')

const router = Router()

router.get('/', () => 'Success!').all('*', () => error(404))

const ittyServer = createServerAdapter((...args) =>
router
.handle(...args)
.then(json)
.catch(error)
)

// Then use it in any environment
const httpServer = createServer(ittyServer)
httpServer.listen(3001)
console.log('listening at https://localhost:3001')
Loading

0 comments on commit a8575eb

Please sign in to comment.