Skip to content

Commit

Permalink
Merge pull request #4 from krutoo/router-builder
Browse files Browse the repository at this point in the history
Router builder method added
  • Loading branch information
krutoo committed Apr 5, 2024
2 parents 38e2be5 + 86d2c9d commit 4d1e275
Show file tree
Hide file tree
Showing 4 changed files with 140 additions and 56 deletions.
97 changes: 48 additions & 49 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -170,75 +170,74 @@ Deno.serve(

You can use utils for simply configure your HTTP server.

**In Bun:**
### In Deno

```ts
import { router, route } from '@krutoo/fetch-tools';
import { router } from '@krutoo/fetch-tools';

Bun.serve({
port: 8080,
fetch: router(
// handler of GET /
route.get('/', () => new Response('Home page')),

// handler of PUT /about
route.put('/about', () => new Response('About page')),
const handler = router
.builder()
.get('/', () => new Response('Home page'))
.put('/about', () => new Response('About page'))
.post('/news', () => new Response('News page'))
.all('/stats', () => new Response('Some stats'))
.build();

// handler of POST /news
route.post('/news', () => new Response('News page')),

// handler for any method
route('/stats', () => new Response('Some stats')),
),
await Deno.serve({
port: 8080,
handler: handler,
});
```

**In Deno:**
### In Bun

```ts
import { serve } from 'https://deno.land/[email protected]/http/server.ts';
import { router, route } from '@krutoo/fetch-tools';

await serve(
router(
route('/', () => new Response('Home page')),
route('/news', () => new Response('News page')),
route('/about', () => new Response('About page')),
),
{ port: 8080 },
);
import { router } from '@krutoo/fetch-tools';

const handler = router
.builder()
.get('/', () => new Response('Home page'))
.put('/about', () => new Response('About page'))
.post('/news', () => new Response('News page'))
.all('/stats', () => new Response('Some stats'))
.build();

Bun.serve({
port: 8080,
fetch: handler,
});
```

**In Node.js (node:http or express):**
### In Node.js (`node:http` or `express`)

Currently there is no builtin server implementation based on fetch API.

Is it possible to use adapter for `node:http` or `express` from [@whatwg-node/server](https://www.npmjs.com/package/@whatwg-node/server).

```ts
import { router, route } from '@krutoo/fetch-tools';
import { router } from '@krutoo/fetch-tools';
import { createServer } from 'node:http';
import { createServerAdapter } from '@whatwg-node/server';
import express from 'express';

const handler = router(
route('/', () => new Response('Home page')),
route('/news', () => new Response('News page')),
route('/about', () => new Response('About page')),
);
const handler = router
.builder()
.get('/', () => new Response('Home page'))
.put('/about', () => new Response('About page'))
.post('/news', () => new Response('News page'))
.all('/stats', () => new Response('Some stats'))
.build();

const app = express();
const server = createServer(createServerAdapter(handler));

app.get('/greeting', createServerAdapter(handler));

app.listen(8080);
server.listen(8080);
```

### Middleware for servers

You can use middleware for server handlers too:

```ts
import { router, route, applyMiddleware } from '@krutoo/fetch-tools';
import { router, applyMiddleware } from '@krutoo/fetch-tools';
import { log } from '@krutoo/fetch-tools/middleware';

const enhance = applyMiddleware(
Expand All @@ -247,17 +246,17 @@ const enhance = applyMiddleware(
}),
);

const handler = enhance(
router(
route('/', () => new Response('Home page')),
route('/news', () => new Response('News page')),
route('/about', () => new Response('About page')),
),
);
const handler = router
.builder()
.get('/', () => new Response('Home page'))
.put('/about', () => new Response('About page'))
.post('/news', () => new Response('News page'))
.all('/stats', () => new Response('Some stats'))
.build();

Bun.serve({
port: 8080,
fetch: handler,
fetch: enhance(handler), // just wrap handler to enhancer for apply middleware
});
```

Expand Down
2 changes: 1 addition & 1 deletion deno.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"./server": "./src/server.ts"
},
"lint": {
"exclude": ["npm/"]
"exclude": ["npm/**/*"]
},
"publish": {
"include": ["LICENSE", "README.md", "src/**/*.ts"],
Expand Down
4 changes: 4 additions & 0 deletions src/configure.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ export function configureFetch<T extends typeof fetch>(
* @returns Enhancer.
*/
export function applyMiddleware(...list: Array<Middleware>): Enhancer {
if (list.length === 0) {
return handler => handler;
}

return handler => {
let result = handler;

Expand Down
93 changes: 87 additions & 6 deletions src/server.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { Handler } from './types.ts';

interface Route {
is(url: URL, request: Request): boolean;
matches(url: URL, request: Request): boolean;
handler: Handler;
}

Expand All @@ -12,7 +12,7 @@ export function router(...routes: Route[]): Handler {
const url = new URL(request.url);

for (const route of routes) {
if (route.is(url, request)) {
if (route.matches(url, request)) {
return route.handler(request);
}
}
Expand All @@ -21,16 +21,18 @@ export function router(...routes: Route[]): Handler {
};
}

router.builder = builder;

export function route(pattern: RoutePattern, handler: Handler): Route {
if (typeof pattern === 'function') {
return {
is: pattern,
matches: pattern,
handler,
};
}

return {
is: url => url.pathname === pattern,
matches: url => url.pathname === pattern,
handler,
};
}
Expand All @@ -51,14 +53,93 @@ function createRouteFactoryForMethod(method: string) {
return (pattern: RoutePattern, handler: Handler): Route => {
if (typeof pattern === 'function') {
return {
is: (url, req) => isSuitableMethod(req) && pattern(url, req),
matches: (url, req) => isSuitableMethod(req) && pattern(url, req),
handler,
};
}

return {
is: (url, request) => isSuitableMethod(request) && url.pathname === pattern,
matches: (url, request) => isSuitableMethod(request) && url.pathname === pattern,
handler,
};
};
}

interface HandlerBuilder {
all(pattern: RoutePattern, handler: Handler): this;
get(pattern: RoutePattern, handler: Handler): this;
post(pattern: RoutePattern, handler: Handler): this;
put(pattern: RoutePattern, handler: Handler): this;
delete(pattern: RoutePattern, handler: Handler): this;
head(pattern: RoutePattern, handler: Handler): this;
options(pattern: RoutePattern, handler: Handler): this;
connect(pattern: RoutePattern, handler: Handler): this;
patch(pattern: RoutePattern, handler: Handler): this;
build(): Handler;
}

function builder(): HandlerBuilder {
const routeList: Array<Route> = [];

const builder: HandlerBuilder = {
all(pattern, handler) {
routeList.push(route.all(pattern, handler));

return builder;
},

get(pattern, handler) {
routeList.push(route.get(pattern, handler));

return builder;
},

post(pattern, handler) {
routeList.push(route.post(pattern, handler));

return builder;
},

put(pattern, handler) {
routeList.push(route.put(pattern, handler));

return builder;
},

delete(pattern, handler) {
routeList.push(route.delete(pattern, handler));

return builder;
},

head(pattern, handler) {
routeList.push(route.head(pattern, handler));

return builder;
},

options(pattern, handler) {
routeList.push(route.options(pattern, handler));

return builder;
},

connect(pattern, handler) {
routeList.push(route.connect(pattern, handler));

return builder;
},

patch(pattern, handler) {
routeList.push(route.patch(pattern, handler));

return builder;
},

build() {
return router(...routeList);
},
};

return builder;
}

0 comments on commit 4d1e275

Please sign in to comment.