Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Router builder method added #4

Merged
merged 1 commit into from
Apr 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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;
}
Loading