Skip to content

Commit 701830e

Browse files
authored
Merge pull request #10 from Bessonov/fix-default-match
add default match to NodeHttpRouter
2 parents 2f67545 + d316007 commit 701830e

File tree

5 files changed

+132
-12
lines changed

5 files changed

+132
-12
lines changed

README.md

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -349,7 +349,53 @@ There are some use cases for nested routers:
349349
- Implement modularity
350350
- Apply middlewares globally
351351

352-
See [example](./src/__tests__/Router.test.ts#216).
352+
An example of multi-tenancy:
353+
354+
```typescript
355+
// create main rooter
356+
const rootRouter = new NodeHttpRouter()
357+
// attach some global urls
358+
// rootRouter.addRoute(...)
359+
360+
// create a router used for all handlers
361+
// with tenant information
362+
const tenantRouter = new Router<{
363+
req: ServerRequest
364+
res: ServerResponse
365+
tenant: string
366+
}>()
367+
368+
// connect routers
369+
rootRouter.addRoute({
370+
matcher: new RegExpUrlMatcher<{
371+
tenant: string
372+
url: string
373+
}>([/^\/auth\/realms\/(?<tenant>[^/]+)(?<url>.+)/]),
374+
handler: ({ data, match }) => {
375+
const { req, res } = data
376+
// figure tenant out
377+
const { tenant, url } = match.result.match.groups
378+
// pass the new url down
379+
req.url = url
380+
return tenantRouter.exec({
381+
req,
382+
res,
383+
tenant,
384+
})
385+
},
386+
})
387+
388+
// attach some urls behind tenant
389+
tenantRouter.addRoute({
390+
matcher: new ExactUrlPathnameMatcher(['/myurl']),
391+
handler: ({ data: { tenant }, match: { result: { pathname } } }) => {
392+
// if requested url is `/auth/realms/mytenant/myurl`, then:
393+
// tenant: mytenant
394+
// pathname: /myurl
395+
return `tenant: ${tenant}, url: ${pathname}`
396+
}
397+
})
398+
```
353399

354400
## License
355401

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@bessonovs/node-http-router",
3-
"version": "2.2.0",
3+
"version": "2.3.0",
44
"description": "Extensible http router for node and micro",
55
"keywords": [
66
"router",

src/__tests__/Router.test.ts

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ import {
99
import {
1010
Test,
1111
} from 'ts-toolbelt'
12+
import {
13+
ServerResponse,
14+
} from 'http'
1215
import {
1316
MatchedHandler,
1417
Router,
@@ -223,6 +226,60 @@ it('simple non-matching router type', () => {
223226
})
224227
})
225228

229+
it('tenant router example from docs', () => {
230+
// create main rooter
231+
const rootRouter = new NodeHttpRouter()
232+
// attach some global urls
233+
// rootRouter.addRoute(...)
234+
235+
// create a router used for all handlers
236+
// with tenant information
237+
const tenantRouter = new Router<{
238+
req: ServerRequest
239+
res: ServerResponse
240+
tenant: string
241+
}>()
242+
243+
// connect routers
244+
rootRouter.addRoute({
245+
matcher: new RegExpUrlMatcher<{
246+
tenant: string
247+
url: string
248+
}>([/^\/auth\/realms\/(?<tenant>[^/]+)(?<url>.+)/]),
249+
handler: ({ data, match }) => {
250+
const { req, res } = data
251+
// figure tenant out
252+
const { tenant, url } = match.result.match.groups
253+
// pass the new url down
254+
req.url = url
255+
return tenantRouter.exec({
256+
req,
257+
res,
258+
tenant,
259+
})
260+
},
261+
})
262+
263+
// attach some urls behind tenant
264+
tenantRouter.addRoute({
265+
matcher: new ExactUrlPathnameMatcher(['/myurl']),
266+
handler: ({ data: { tenant }, match: { result: { pathname } } }) => {
267+
// if requested url is `/auth/realms/mytenant/myurl`, then:
268+
// tenant: mytenant
269+
// pathname: /myurl
270+
return `tenant: ${tenant}, url: ${pathname}`
271+
},
272+
})
273+
274+
expect(rootRouter.serve(
275+
createRequest({
276+
method: 'GET',
277+
url: '/auth/realms/mytenant/myurl',
278+
}),
279+
createResponse(),
280+
)).toBe('tenant: mytenant, url: /myurl')
281+
})
282+
226283
it('nested router', () => {
227284
const appRouter = new Router<{
228285
tenant: string

src/node/NodeHttpRouter.ts

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,30 +3,31 @@ import {
33
ServerResponse,
44
} from 'http'
55
import {
6+
MatchResult,
67
MatchedResult,
78
} from '../matchers'
89
import {
10+
Handler,
911
Router,
1012
} from '../Router'
1113
import {
1214
ServerRequest,
1315
toServerRequest,
1416
} from './ServerRequest'
1517

18+
interface ServerRequestResponse {
19+
req: ServerRequest
20+
res: ServerResponse
21+
}
22+
1623
export interface NodeHttpRouterParams<MR> {
17-
data: {
18-
req: ServerRequest
19-
res: ServerResponse
20-
}
24+
data: ServerRequestResponse
2125
match: MatchedResult<MR>
2226
}
2327

24-
export class NodeHttpRouter extends Router<{
25-
req: ServerRequest
26-
res: ServerResponse
27-
}> {
28-
constructor() {
29-
super()
28+
export class NodeHttpRouter extends Router<ServerRequestResponse> {
29+
constructor(defaultHandler?: Handler<MatchResult<unknown>, ServerRequestResponse>) {
30+
super(defaultHandler)
3031
this.serve = this.serve.bind(this)
3132
}
3233

src/node/__tests__/NodeHttpRouter.test.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ import {
22
IncomingMessage,
33
ServerResponse,
44
} from 'http'
5+
import {
6+
ExactUrlPathnameMatcher,
7+
} from '../../matchers'
58
import {
69
NodeHttpRouter,
710
} from '../NodeHttpRouter'
@@ -15,3 +18,16 @@ it('missing url in request', () => {
1518
const nodeRouter = new NodeHttpRouter()
1619
expect(() => nodeRouter.serve({ method: 'GET' } as IncomingMessage, {} as ServerResponse)).toThrowError(`request missing 'url'`)
1720
})
21+
22+
it('default handler', () => {
23+
const nodeRouter = new NodeHttpRouter(({ data: { req } }) => {
24+
return `not found url: ${req.url}`
25+
})
26+
nodeRouter.addRoute({
27+
matcher: new ExactUrlPathnameMatcher(['/test']),
28+
handler: () => 'test url',
29+
})
30+
31+
expect(nodeRouter.serve({ method: 'GET', url: '/test' } as IncomingMessage, {} as ServerResponse)).toBe('test url')
32+
expect(nodeRouter.serve({ method: 'GET', url: '/404' } as IncomingMessage, {} as ServerResponse)).toBe('not found url: /404')
33+
})

0 commit comments

Comments
 (0)