bun-router 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/examples/basic.ts +1 -6
- package/examples/dynamic.ts +12 -17
- package/lib/http/generic-methods.ts +10 -2
- package/lib/logger/color.ts +1 -1
- package/lib/logger/logger.ts +9 -0
- package/lib/router/router.d.ts +20 -19
- package/lib/router/router.ts +38 -96
- package/lib/router/tree.ts +90 -0
- package/package.json +1 -1
- package/tests/router.test.ts +0 -64
- package/examples/sqlite.ts +0 -22
package/README.md
CHANGED
package/examples/basic.ts
CHANGED
@@ -6,13 +6,8 @@ r.add('/', 'GET', () => http.json(200, 'ok'));
|
|
6
6
|
|
7
7
|
r.add('/user/:name', 'GET', (ctx) => {
|
8
8
|
const name = ctx.params.get('name');
|
9
|
+
if (!name) return http.json(500, 'no name');
|
9
10
|
return http.json(200, name);
|
10
11
|
});
|
11
12
|
|
12
|
-
r.add('/user/:name/:id', 'GET', (ctx) => {
|
13
|
-
const name = ctx.params.get('name');
|
14
|
-
const id = ctx.params.get('id');
|
15
|
-
return http.json(200, {name: name, id: id});
|
16
|
-
});
|
17
|
-
|
18
13
|
r.serve();
|
package/examples/dynamic.ts
CHANGED
@@ -1,29 +1,24 @@
|
|
1
1
|
import { router, http } from '..';
|
2
2
|
import { Context } from '../lib/router/router.d';
|
3
3
|
|
4
|
-
const
|
5
|
-
const name = ctx.params.get('name');
|
6
|
-
if (typeof name === 'undefined' || name === '') return http.html(500, '<h6 style="color: red">User Undefined</h6>');
|
7
|
-
return http.html(200, `<h4>Hello, ${name}!</h4>`);
|
8
|
-
}
|
4
|
+
const home = (ctx: Context) => new Response('Welcome Home', { status: 200 });
|
9
5
|
|
10
|
-
const
|
11
|
-
const
|
12
|
-
if (
|
13
|
-
return http.
|
6
|
+
const subreddit = (ctx: Context) => {
|
7
|
+
const sub = ctx.params.get('subreddit');
|
8
|
+
if (!sub) return http.json(400, { error: 'no subreddit provided' });
|
9
|
+
return http.json(200, { subreddit: sub });
|
14
10
|
}
|
15
11
|
|
16
|
-
const
|
17
|
-
const
|
18
|
-
if (
|
19
|
-
return http.
|
12
|
+
const user = (ctx: Context) => {
|
13
|
+
const user = ctx.params.get('user');
|
14
|
+
if (!user) return http.json(400, { error: 'no user provided' });
|
15
|
+
return http.json(200, { user: user });
|
20
16
|
}
|
21
17
|
|
22
18
|
const r = router();
|
23
19
|
|
24
|
-
r.add('/
|
25
|
-
r.add('/
|
26
|
-
r.add('/u/:
|
27
|
-
|
20
|
+
r.add('/', 'GET', home);
|
21
|
+
r.add('/r/:subreddit', 'GET', subreddit);
|
22
|
+
r.add('/u/:user', 'GET', user);
|
28
23
|
|
29
24
|
r.serve();
|
@@ -10,8 +10,7 @@ const http = {
|
|
10
10
|
}));
|
11
11
|
},
|
12
12
|
html: async (statusCode: number, content: string): Promise<Response> => {
|
13
|
-
|
14
|
-
return Promise.resolve(new Response(Bun.escapeHTML(content), {
|
13
|
+
return Promise.resolve(new Response(content, {
|
15
14
|
status: statusCode,
|
16
15
|
statusText: httpStatusCodes[statusCode],
|
17
16
|
headers: {'Content-Type': 'text/html; charset=utf-8'}
|
@@ -50,6 +49,15 @@ const http = {
|
|
50
49
|
|
51
50
|
return Promise.resolve(response);
|
52
51
|
},
|
52
|
+
methodNotAllowed: async (msg?: string): Promise<Response> => {
|
53
|
+
const response = new Response(msg ?? 'method not allowed', {
|
54
|
+
status: 405,
|
55
|
+
statusText: httpStatusCodes[405],
|
56
|
+
headers: {'Content-Type': 'text/html'},
|
57
|
+
});
|
58
|
+
|
59
|
+
return Promise.resolve(response);
|
60
|
+
},
|
53
61
|
message: async (status: number, msg?: string): Promise<Response> => {
|
54
62
|
const response = new Response(msg ?? '?', {
|
55
63
|
status: status,
|
package/lib/logger/color.ts
CHANGED
package/lib/logger/logger.ts
CHANGED
@@ -3,6 +3,14 @@ import {Logger} from './logger.d';
|
|
3
3
|
|
4
4
|
const pad = (n: number) => String(n).padStart(2, '0');
|
5
5
|
|
6
|
+
const TITLE = `
|
7
|
+
_ _
|
8
|
+
| |_ _ _ ___ ___ ___ _ _| |_ ___ ___
|
9
|
+
| . | | | | | _| . | | | _| -_| _|
|
10
|
+
|___|___|_|_| |_| |___|___|_| |___|_|
|
11
|
+
|
12
|
+
`
|
13
|
+
|
6
14
|
const timestamp = (date: Date) => {
|
7
15
|
const month = pad(date.getMonth());
|
8
16
|
const day = pad(date.getDate());
|
@@ -48,6 +56,7 @@ const logger = (): Logger => {
|
|
48
56
|
const portColor = color('green', 'bgBlack', String(port));
|
49
57
|
const msg = `${source}: Starting Server on :${portColor}\n`;
|
50
58
|
|
59
|
+
await Bun.write(Bun.stdout, TITLE);
|
51
60
|
await Bun.write(Bun.stdout, msg);
|
52
61
|
},
|
53
62
|
info: async (statusCode: number, routePath: string, method: string, message?: string) => {
|
package/lib/router/router.d.ts
CHANGED
@@ -2,25 +2,28 @@ import { TLSOptions, TLSWebSocketServeOptions, WebSocketServeOptions, ServeOptio
|
|
2
2
|
import { Logger } from '../logger/logger';
|
3
3
|
import { Database } from 'bun:sqlite';
|
4
4
|
|
5
|
+
type HttpHandler = (ctx: Context) => Response | Promise<Response>
|
5
6
|
|
6
7
|
type Context = {
|
7
|
-
cookies: Map<string, string
|
8
|
-
db
|
9
|
-
formData: FormData | Promise<FormData> | undefined
|
10
|
-
json: (statusCode: number, data: any) => Response | Promise<Response
|
11
|
-
logger: Logger
|
12
|
-
params: Map<string, string
|
13
|
-
query: URLSearchParams
|
14
|
-
request: Request
|
15
|
-
|
16
|
-
token?: string,
|
8
|
+
cookies: Map<string, string>;
|
9
|
+
db?: Database;
|
10
|
+
formData: FormData | Promise<FormData> | undefined;
|
11
|
+
json: (statusCode: number, data: any) => Response | Promise<Response>;
|
12
|
+
logger: Logger;
|
13
|
+
params: Map<string, string>;
|
14
|
+
query: URLSearchParams;
|
15
|
+
request: Request;
|
16
|
+
token?: string;
|
17
17
|
};
|
18
18
|
|
19
19
|
|
20
20
|
type Route = {
|
21
|
-
|
22
|
-
|
23
|
-
|
21
|
+
children: Map<string, Route>;
|
22
|
+
path: string;
|
23
|
+
dynamicPath: string;
|
24
|
+
method: string;
|
25
|
+
handler: HttpHandler;
|
26
|
+
isLast: boolean;
|
24
27
|
}
|
25
28
|
|
26
29
|
type Options = {
|
@@ -35,13 +38,11 @@ type RouterOptions<Options> = ServeOptions
|
|
35
38
|
|
36
39
|
|
37
40
|
type Router = (port?: number | string, options?: RouterOptions) => {
|
38
|
-
add: (pattern: string, method: string, callback: (req: Context) => Response | Promise<Response>) => void
|
39
|
-
|
40
|
-
|
41
|
-
static: (pattern: string, root: string) => void,
|
42
|
-
serve: () => void,
|
41
|
+
add: (pattern: string, method: string, callback: (req: Context) => Response | Promise<Response>) => void;
|
42
|
+
static: (pattern: string, root: string) => void;
|
43
|
+
serve: () => void;
|
43
44
|
}
|
44
45
|
|
45
46
|
|
46
47
|
|
47
|
-
export { Context , Route, Router, RouterOptions, Options }
|
48
|
+
export { Context , Route, Router, RouterOptions, Options, HttpHandler }
|
package/lib/router/router.ts
CHANGED
@@ -1,17 +1,15 @@
|
|
1
1
|
import path from 'path';
|
2
2
|
import { Database } from 'bun:sqlite';
|
3
|
-
import { Route, Router, Context, RouterOptions, Options } from './router.d';
|
3
|
+
import { Route, Router, Context, RouterOptions, Options, HttpHandler } from './router.d';
|
4
4
|
import { httpStatusCodes } from '../http/status';
|
5
5
|
import { readDir } from '../fs/fsys';
|
6
6
|
import { logger } from '../logger/logger';
|
7
|
-
import { Logger } from '../logger/logger.d';
|
8
7
|
import { http } from '../http/generic-methods';
|
8
|
+
import {Radix, createContext} from './tree';
|
9
9
|
|
10
|
-
|
11
|
-
// if the route pattern is /:foo and the request URL is /bar: {foo: 'bar'}
|
12
|
-
const extract = (route: Route, ctx: Context) => {
|
10
|
+
const extract = (path: string, ctx: Context) => {
|
13
11
|
const url = new URL(ctx.request.url);
|
14
|
-
const pathSegments =
|
12
|
+
const pathSegments = path.split('/');
|
15
13
|
const urlSegments = url.pathname.split('/');
|
16
14
|
|
17
15
|
if (pathSegments.length !== urlSegments.length) return
|
@@ -19,10 +17,10 @@ const extract = (route: Route, ctx: Context) => {
|
|
19
17
|
return {
|
20
18
|
params: () => {
|
21
19
|
for (let i = 0; i < pathSegments.length; i++) {
|
22
|
-
if
|
20
|
+
if((pathSegments[i][0] === ':')) {
|
23
21
|
const k = pathSegments[i].replace(':', '');
|
24
22
|
const v = urlSegments[i];
|
25
|
-
ctx.params.set(k,
|
23
|
+
ctx.params.set(k,v);
|
26
24
|
}
|
27
25
|
}
|
28
26
|
}
|
@@ -30,67 +28,16 @@ const extract = (route: Route, ctx: Context) => {
|
|
30
28
|
|
31
29
|
}
|
32
30
|
|
33
|
-
// ensure the route pattern matches the request URL
|
34
|
-
const match = (route: Route, ctx: Context): boolean => {
|
35
|
-
const url = new URL(ctx.request.url);
|
36
|
-
const patternRegex = new RegExp('^' + route.pattern.replace(/:[^/]+/g, '([^/]+)') + '$');
|
37
|
-
const matches = url.pathname.match(patternRegex);
|
38
|
-
|
39
|
-
if (matches && route.method === ctx.request.method) {
|
40
|
-
const extractor = extract(route, ctx);
|
41
|
-
extractor?.params();
|
42
|
-
|
43
|
-
return true;
|
44
|
-
}
|
45
|
-
|
46
|
-
return false;
|
47
|
-
}
|
48
|
-
|
49
|
-
// set the context for the reuest
|
50
|
-
const setContext = (req: Request, lgr: Logger, opts: Options, route: Route): Context => {
|
51
|
-
const token = req.headers.get('Authorization');
|
52
|
-
return {
|
53
|
-
token: token ?? '',
|
54
|
-
cookies: new Map(),
|
55
|
-
formData: req.formData(),
|
56
|
-
request: req,
|
57
|
-
params: new Map(),
|
58
|
-
query: new URL(req.url).searchParams,
|
59
|
-
db: new Database(opts.db ?? ':memory:'),
|
60
|
-
logger: lgr,
|
61
|
-
route: route,
|
62
|
-
json: (statusCode: number, data: any) => http.json(statusCode, data),
|
63
|
-
}
|
64
|
-
}
|
65
|
-
|
66
31
|
const router: Router = (port?: number | string, options?: RouterOptions<Options>) => {
|
67
|
-
const
|
32
|
+
const {addRoute, findRoute} = Radix();
|
68
33
|
const lgr = logger();
|
69
34
|
|
70
35
|
return {
|
71
|
-
// add a
|
72
|
-
add: (pattern: string, method: string, callback:
|
73
|
-
|
74
|
-
pattern: pattern,
|
75
|
-
method: method,
|
76
|
-
callback: callback,
|
77
|
-
})
|
78
|
-
},
|
79
|
-
GET: (pattern: string, callback: (ctx: Context) => Response | Promise<Response>) => {
|
80
|
-
routes.push({
|
81
|
-
pattern: pattern,
|
82
|
-
method: 'GET',
|
83
|
-
callback: callback,
|
84
|
-
});
|
85
|
-
},
|
86
|
-
POST: (pattern: string, callback: (ctx: Context) => Response | Promise<Response>) => {
|
87
|
-
routes.push({
|
88
|
-
pattern: pattern,
|
89
|
-
method: 'POST',
|
90
|
-
callback: callback,
|
91
|
-
});
|
36
|
+
// add a route to the router tree
|
37
|
+
add: (pattern: string, method: string, callback: HttpHandler) => {
|
38
|
+
addRoute(pattern, method, callback);
|
92
39
|
},
|
93
|
-
// add a route
|
40
|
+
// add a static route to the router tree
|
94
41
|
static: async (pattern: string, root: string) => {
|
95
42
|
await readDir(root, async (fp, _) => {
|
96
43
|
const pure = path.join('.', fp);
|
@@ -107,12 +54,15 @@ const router: Router = (port?: number | string, options?: RouterOptions<Options>
|
|
107
54
|
if (base === 'index') patternPath = pattern;
|
108
55
|
|
109
56
|
const route: Route = {
|
110
|
-
|
57
|
+
children: new Map(),
|
58
|
+
dynamicPath: '',
|
59
|
+
isLast: true,
|
60
|
+
path: patternPath,
|
111
61
|
method: 'GET',
|
112
|
-
|
62
|
+
handler: async () => await http.file(200, pure),
|
113
63
|
};
|
114
64
|
|
115
|
-
|
65
|
+
addRoute(route.path, 'GET', route.handler);
|
116
66
|
});
|
117
67
|
},
|
118
68
|
// start the server
|
@@ -120,52 +70,44 @@ const router: Router = (port?: number | string, options?: RouterOptions<Options>
|
|
120
70
|
lgr.start(port ?? 3000);
|
121
71
|
let opts: Options = { db: ':memory:' };
|
122
72
|
|
73
|
+
// TODO: add support for TLS and WebSockets
|
123
74
|
Bun.serve({
|
124
75
|
port: port ?? 3000,
|
125
76
|
...options,
|
126
77
|
async fetch(req) {
|
127
78
|
const url = new URL(req.url);
|
79
|
+
let path = url.pathname;
|
128
80
|
|
81
|
+
// set the database
|
129
82
|
if (options) {
|
130
83
|
let o = options as Options;
|
131
84
|
opts.db = o.db;
|
132
85
|
}
|
133
86
|
|
134
|
-
|
87
|
+
const route = findRoute(path);
|
135
88
|
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
89
|
+
// if the route exists, execute the handler
|
90
|
+
if (route) {
|
91
|
+
if (route.method !== req.method) {
|
92
|
+
lgr.info(405, url.pathname, req.method, httpStatusCodes[405]);
|
93
|
+
return Promise.resolve(http.methodNotAllowed());
|
94
|
+
}
|
142
95
|
|
143
|
-
|
144
|
-
|
145
|
-
for (const [key, value] of ctx.cookies) {
|
146
|
-
cookieValue.push(`${key}=${value}`);
|
147
|
-
}
|
148
|
-
}
|
96
|
+
const context = createContext(path, route, req);
|
97
|
+
context.db = new Database(opts.db);
|
149
98
|
|
150
|
-
|
99
|
+
const response = await route.handler(context);
|
151
100
|
|
152
|
-
|
101
|
+
lgr.info(response.status, url.pathname, req.method, httpStatusCodes[response.status]);
|
102
|
+
return Promise.resolve(response);
|
103
|
+
}
|
153
104
|
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
statusText: httpStatusCodes[405]
|
160
|
-
});
|
161
|
-
lgr.info(405, route.pattern, req.method, httpStatusCodes[405])
|
162
|
-
return Promise.resolve(res);
|
163
|
-
}
|
164
|
-
}
|
165
|
-
}
|
105
|
+
// if no route is found, return 404
|
106
|
+
const response = await http.notFound();
|
107
|
+
|
108
|
+
lgr.info(response.status, url.pathname, req.method, httpStatusCodes[response.status]);
|
109
|
+
return Promise.resolve(http.notFound());
|
166
110
|
|
167
|
-
lgr.info(statusCode, url.pathname, req.method, httpStatusCodes[statusCode]);
|
168
|
-
return Promise.resolve(http.message(statusCode, httpStatusCodes[statusCode]));
|
169
111
|
}
|
170
112
|
});
|
171
113
|
},
|
@@ -0,0 +1,90 @@
|
|
1
|
+
import { HttpHandler, Context, Route } from "./router.d";
|
2
|
+
import { http } from "../http/generic-methods";
|
3
|
+
|
4
|
+
const splitPath = (s: string): string[] => s.split('/').filter(x => x !== '');
|
5
|
+
|
6
|
+
const createRoute = (path: string, method: string, handler: HttpHandler): Route => {
|
7
|
+
const route: Route = {
|
8
|
+
children: new Map(),
|
9
|
+
path: path,
|
10
|
+
dynamicPath: '',
|
11
|
+
method: method,
|
12
|
+
handler: handler,
|
13
|
+
isLast: false
|
14
|
+
};
|
15
|
+
|
16
|
+
return route;
|
17
|
+
};
|
18
|
+
|
19
|
+
const extractParams = (path: string, route: Route, params: Map<string, string>) => {
|
20
|
+
const pathParts = splitPath(path);
|
21
|
+
const routeParts = splitPath(route.path);
|
22
|
+
|
23
|
+
for (let i = 0; i < routeParts.length; i++) {
|
24
|
+
const part = routeParts[i];
|
25
|
+
if (part.startsWith(':')) {
|
26
|
+
params.set(part.slice(1), pathParts[i]);
|
27
|
+
}
|
28
|
+
}
|
29
|
+
};
|
30
|
+
|
31
|
+
const createContext = (path: string, route: Route, req: Request): Context => {
|
32
|
+
const params: Map<string, string> = new Map();
|
33
|
+
|
34
|
+
if (route) extractParams(path, route, params);
|
35
|
+
|
36
|
+
return {
|
37
|
+
params: params,
|
38
|
+
request: req,
|
39
|
+
query: new URLSearchParams(path),
|
40
|
+
cookies: new Map(),
|
41
|
+
formData: undefined,
|
42
|
+
logger: undefined,
|
43
|
+
json: (statusCode: number, data: any) => http.json(statusCode, data),
|
44
|
+
}
|
45
|
+
};
|
46
|
+
|
47
|
+
const Radix = () => {
|
48
|
+
let root = createRoute('', 'GET', () => http.notFound());
|
49
|
+
|
50
|
+
const addRoute = (path: string, method: string, handler: HttpHandler) => {
|
51
|
+
const pathParts = splitPath(path);
|
52
|
+
let current = root;
|
53
|
+
|
54
|
+
for (let i = 0; i < pathParts.length; i++) {
|
55
|
+
const part = pathParts[i];
|
56
|
+
if (part.startsWith(':')) {
|
57
|
+
current.dynamicPath = part;
|
58
|
+
}
|
59
|
+
if (!current.children.has(part)) {
|
60
|
+
current.children.set(part, createRoute(part, method, handler));
|
61
|
+
}
|
62
|
+
current = current.children.get(part)!;
|
63
|
+
}
|
64
|
+
|
65
|
+
current.handler = handler;
|
66
|
+
current.isLast = true;
|
67
|
+
current.path = path;
|
68
|
+
};
|
69
|
+
|
70
|
+
const findRoute = (path: string): Route | undefined => {
|
71
|
+
const pathParts = splitPath(path);
|
72
|
+
let current = root;
|
73
|
+
for (let i = 0; i < pathParts.length; i++) {
|
74
|
+
const part = pathParts[i];
|
75
|
+
if (current.children.has(part)) {
|
76
|
+
current = current.children.get(part)!;
|
77
|
+
} else if (current.dynamicPath) {
|
78
|
+
current = current.children.get(current.dynamicPath)!;
|
79
|
+
} else {
|
80
|
+
return;
|
81
|
+
}
|
82
|
+
}
|
83
|
+
return current;
|
84
|
+
}
|
85
|
+
|
86
|
+
return { addRoute, findRoute }
|
87
|
+
|
88
|
+
};
|
89
|
+
|
90
|
+
export { Radix, createContext }
|
package/package.json
CHANGED
package/tests/router.test.ts
CHANGED
@@ -1,68 +1,4 @@
|
|
1
1
|
import { describe, test, expect } from 'bun:test';
|
2
|
-
import { extract } from '..';
|
3
|
-
import { Context, Route } from '../lib/router/router.d';
|
4
|
-
|
5
|
-
describe('URL Params', () => {
|
6
|
-
test('/user/:name', () => {
|
7
|
-
const route: Route = {
|
8
|
-
pattern: '/user/:name',
|
9
|
-
method: 'GET',
|
10
|
-
callback: () => new Response('ok'),
|
11
|
-
};
|
12
|
-
|
13
|
-
const ctx: Context = {
|
14
|
-
request: new Request('http://localhost:3000/user/foo'),
|
15
|
-
params: new Map(),
|
16
|
-
};
|
17
|
-
|
18
|
-
const extractor = extract(route, ctx);
|
19
|
-
|
20
|
-
extractor?.params();
|
21
|
-
|
22
|
-
const name = ctx.params.get('name');
|
23
|
-
expect(name).toBe('foo');
|
24
|
-
});
|
25
|
-
|
26
|
-
test('/user/:name/:id', () => {
|
27
|
-
const route: Route = {
|
28
|
-
pattern: '/user/:name/:id',
|
29
|
-
method: 'GET',
|
30
|
-
callback: () => new Response('ok'),
|
31
|
-
};
|
32
|
-
|
33
|
-
const ctx: Context = {
|
34
|
-
request: new Request('http://localhost:3000/user/foo/123'),
|
35
|
-
params: new Map(),
|
36
|
-
};
|
37
|
-
|
38
|
-
const extractor = extract(route, ctx);
|
39
|
-
|
40
|
-
extractor?.params();
|
41
|
-
|
42
|
-
const name = ctx.params.get('name');
|
43
|
-
const id = ctx.params.get('id');
|
44
|
-
|
45
|
-
expect(name).toBe('foo');
|
46
|
-
expect(id).toBe('123');
|
47
|
-
});
|
48
|
-
|
49
|
-
test('/foo', () => {
|
50
|
-
const route: Route = {
|
51
|
-
pattern: '/foo',
|
52
|
-
method: 'GET',
|
53
|
-
callback: () => new Response('ok'),
|
54
|
-
}
|
55
|
-
|
56
|
-
const ctx: Context = {
|
57
|
-
request: new Request('http://localhost:3000/foo'),
|
58
|
-
params: new Map(),
|
59
|
-
}
|
60
|
-
|
61
|
-
const url = new URL(ctx.request.url);
|
62
|
-
|
63
|
-
expect(url.pathname).toBe(route.pattern);
|
64
|
-
});
|
65
|
-
});
|
66
2
|
|
67
3
|
describe('Router', () => {
|
68
4
|
test('Serve', async () => {
|
package/examples/sqlite.ts
DELETED
@@ -1,22 +0,0 @@
|
|
1
|
-
import { router, http } from '..';
|
2
|
-
|
3
|
-
const r = router(3000, {db: './examples/dbs/test.db'});
|
4
|
-
|
5
|
-
r.add('/u/new/:name', 'GET', (ctx) => {
|
6
|
-
const name = ctx.params.get('name');
|
7
|
-
const rando = Math.floor(Math.random()*1000);
|
8
|
-
|
9
|
-
ctx.db.run(`INSERT INTO test VALUES(${rando}, "${name}")`);
|
10
|
-
|
11
|
-
return http.json(200, {message: 'ok'});
|
12
|
-
});
|
13
|
-
|
14
|
-
r.add('/u/:name', 'GET', (ctx) => {
|
15
|
-
const name = ctx.params.get('name');
|
16
|
-
const data = ctx.db.query(`SELECT * FROM test WHERE name = "${name}";`).get();
|
17
|
-
const d = data as {id: number, name: string};
|
18
|
-
|
19
|
-
return d ? http.json(200, d) : new Response('not found', {status: 404});
|
20
|
-
});
|
21
|
-
|
22
|
-
r.serve();
|