kempo-server 1.4.3 → 1.4.6
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/.github/copilot-instructions.md +96 -96
- package/.github/workflows/publish-npm.yml +45 -0
- package/README.md +650 -650
- package/builtinMiddleware.js +136 -136
- package/defaultConfig.js +129 -129
- package/docs/.config.json +5 -5
- package/docs/.config.json.example +19 -19
- package/docs/api/user/[id]/GET.js +15 -15
- package/docs/api/user/[id]/[info]/DELETE.js +12 -12
- package/docs/api/user/[id]/[info]/GET.js +17 -17
- package/docs/api/user/[id]/[info]/POST.js +18 -18
- package/docs/api/user/[id]/[info]/PUT.js +19 -19
- package/docs/configuration.html +119 -119
- package/docs/examples.html +201 -201
- package/docs/getting-started.html +72 -72
- package/docs/index.html +81 -81
- package/docs/manifest.json +87 -87
- package/docs/middleware.html +147 -147
- package/docs/request-response.html +95 -95
- package/docs/routing.html +77 -77
- package/example-middleware.js +23 -23
- package/example.config.json +50 -50
- package/findFile.js +138 -138
- package/getFiles.js +72 -72
- package/getFlags.js +34 -34
- package/index.js +47 -47
- package/middlewareRunner.js +25 -25
- package/package.json +10 -6
- package/requestWrapper.js +87 -87
- package/responseWrapper.js +204 -204
- package/router.js +285 -285
- package/serveFile.js +71 -71
- package/tests/builtinMiddleware-cors.node-test.js +17 -17
- package/tests/builtinMiddleware.node-test.js +74 -74
- package/tests/defaultConfig.node-test.js +13 -13
- package/tests/example-middleware.node-test.js +31 -31
- package/tests/findFile.node-test.js +46 -46
- package/tests/getFiles.node-test.js +25 -25
- package/tests/getFlags.node-test.js +30 -30
- package/tests/index.node-test.js +23 -23
- package/tests/middlewareRunner.node-test.js +18 -18
- package/tests/requestWrapper.node-test.js +51 -51
- package/tests/responseWrapper.node-test.js +74 -74
- package/tests/router-middleware.node-test.js +46 -46
- package/tests/router.node-test.js +88 -88
- package/tests/serveFile.node-test.js +52 -52
- package/tests/test-utils.js +106 -106
|
@@ -1,95 +1,95 @@
|
|
|
1
|
-
<html lang="en" theme="auto">
|
|
2
|
-
<head>
|
|
3
|
-
<meta charset='utf-8'>
|
|
4
|
-
<meta http-equiv='X-UA-Compatible' content='IE=edge'>
|
|
5
|
-
<title>Request & Response - Kempo Server</title>
|
|
6
|
-
<meta name='viewport' content='width=device-width, initial-scale=1'>
|
|
7
|
-
<link rel="icon" type="image/png" sizes="48x48" href="media/icon48.png">
|
|
8
|
-
<link rel="manifest" href="manifest.json">
|
|
9
|
-
<link rel="stylesheet" href="essential.css" />
|
|
10
|
-
</head>
|
|
11
|
-
<body>
|
|
12
|
-
<main>
|
|
13
|
-
<a href="./" class="btn">Home</a>
|
|
14
|
-
<h1>Request & Response Objects</h1>
|
|
15
|
-
<p>Learn how to work with HTTP requests and responses in Kempo Server.</p>
|
|
16
|
-
|
|
17
|
-
<h2>Request Object</h2>
|
|
18
|
-
<p>Kempo Server provides a request object that makes working with HTTP requests easier:</p>
|
|
19
|
-
|
|
20
|
-
<h3>Properties</h3>
|
|
21
|
-
<ul>
|
|
22
|
-
<li><code>request.params</code> - Route parameters from dynamic routes (e.g., <code>{ id: "123", info: "info" }</code>)</li>
|
|
23
|
-
<li><code>request.query</code> - Query string parameters as an object (e.g., <code>{ page: "1", limit: "10" }</code>)</li>
|
|
24
|
-
<li><code>request.path</code> - The pathname of the request URL</li>
|
|
25
|
-
<li><code>request.method</code> - HTTP method (GET, POST, etc.)</li>
|
|
26
|
-
<li><code>request.headers</code> - Request headers object</li>
|
|
27
|
-
<li><code>request.url</code> - Full request URL</li>
|
|
28
|
-
</ul>
|
|
29
|
-
|
|
30
|
-
<h3>Methods</h3>
|
|
31
|
-
<ul>
|
|
32
|
-
<li><code>await request.json()</code> - Parse request body as JSON</li>
|
|
33
|
-
<li><code>await request.text()</code> - Get request body as text</li>
|
|
34
|
-
<li><code>await request.body()</code> - Get raw request body as string</li>
|
|
35
|
-
<li><code>await request.buffer()</code> - Get request body as Buffer</li>
|
|
36
|
-
<li><code>request.get(headerName)</code> - Get specific header value</li>
|
|
37
|
-
<li><code>request.is(type)</code> - Check if content-type contains specified type</li>
|
|
38
|
-
</ul>
|
|
39
|
-
|
|
40
|
-
<h2>Response Object</h2>
|
|
41
|
-
<p>Kempo Server also provides a response object that makes sending responses easier:</p>
|
|
42
|
-
|
|
43
|
-
<h3>Methods</h3>
|
|
44
|
-
<ul>
|
|
45
|
-
<li><code>response.json(object)</code> - Send JSON response with automatic content-type</li>
|
|
46
|
-
<li><code>response.send(data)</code> - Send response (auto-detects content type)</li>
|
|
47
|
-
<li><code>response.html(htmlString)</code> - Send HTML response</li>
|
|
48
|
-
<li><code>response.text(textString)</code> - Send plain text response</li>
|
|
49
|
-
<li><code>response.status(code)</code> - Set status code (chainable)</li>
|
|
50
|
-
<li><code>response.set(field, value)</code> - Set header (chainable)</li>
|
|
51
|
-
<li><code>response.type(contentType)</code> - Set content type (chainable)</li>
|
|
52
|
-
<li><code>response.redirect(url, statusCode)</code> - Redirect to URL</li>
|
|
53
|
-
<li><code>response.cookie(name, value, options)</code> - Set cookie</li>
|
|
54
|
-
<li><code>response.clearCookie(name, options)</code> - Clear cookie</li>
|
|
55
|
-
</ul>
|
|
56
|
-
|
|
57
|
-
<h2>Request Examples</h2>
|
|
58
|
-
|
|
59
|
-
<h3>Accessing Route Parameters</h3>
|
|
60
|
-
<pre><code class="hljs javascript"><span class="hljs-comment">// api/user/[id]/GET.js</span><br /><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">request, response</span>) </span>{<br /> <span class="hljs-keyword">const</span> { id } = request.params;<br /> <span class="hljs-keyword">const</span> { includeDetails } = request.query;<br /> <br /> <span class="hljs-comment">// Fetch user data from database</span><br /> <span class="hljs-keyword">const</span> userData = <span class="hljs-keyword">await</span> getUserById(id);<br /> <br /> <span class="hljs-keyword">if</span> (!userData) {<br /> <span class="hljs-keyword">return</span> response.status(<span class="hljs-number">404</span>).json({ error: <span class="hljs-string">'User not found'</span> });<br /> }<br /> <br /> response.json(userData);<br />}</code></pre>
|
|
61
|
-
|
|
62
|
-
<h3>Handling JSON Data</h3>
|
|
63
|
-
<pre><code class="hljs javascript"><span class="hljs-comment">// api/user/[id]/POST.js</span><br /><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">request, response</span>) </span>{<br /> <span class="hljs-keyword">const</span> { id } = request.params;<br /> <br /> <span class="hljs-keyword">try</span> {<br /> <span class="hljs-keyword">const</span> updateData = <span class="hljs-keyword">await</span> request.json();<br /> <br /> <span class="hljs-comment">// Update user in database</span><br /> <span class="hljs-keyword">const</span> updatedUser = <span class="hljs-keyword">await</span> updateUser(id, updateData);<br /> <br /> response.json(updatedUser);<br /> } <span class="hljs-keyword">catch</span> (error) {<br /> response.status(<span class="hljs-number">400</span>).json({ error: <span class="hljs-string">'Invalid JSON'</span> });<br /> }<br />}</code></pre>
|
|
64
|
-
|
|
65
|
-
<h3>Working with Form Data</h3>
|
|
66
|
-
<pre><code class="hljs javascript"><span class="hljs-comment">// contact/POST.js</span><br /><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">request, response</span>) </span>{<br /> <span class="hljs-keyword">try</span> {<br /> <span class="hljs-keyword">const</span> body = <span class="hljs-keyword">await</span> request.text();<br /> <span class="hljs-keyword">const</span> formData = <span class="hljs-keyword">new</span> URLSearchParams(body);<br /> <span class="hljs-keyword">const</span> name = formData.get(<span class="hljs-string">'name'</span>);<br /> <span class="hljs-keyword">const</span> email = formData.get(<span class="hljs-string">'email'</span>);<br /> <span class="hljs-keyword">const</span> message = formData.get(<span class="hljs-string">'message'</span>);<br /> <br /> <span class="hljs-comment">// Process form data...</span><br /> <span class="hljs-keyword">await</span> sendContactEmail({ name, email, message });<br /> <br /> response.html(<span class="hljs-string">'<h1>Thank you for your message!</h1>'</span>);<br /> } <span class="hljs-keyword">catch</span> (error) {<br /> response.status(<span class="hljs-number">400</span>).html(<span class="hljs-string">'<h1>Error processing form</h1>'</span>);<br /> }<br />}</code></pre>
|
|
67
|
-
|
|
68
|
-
<h3>Checking Headers</h3>
|
|
69
|
-
<pre><code class="hljs javascript"><span class="hljs-comment">// api/upload/POST.js</span><br /><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">request, response</span>) </span>{<br /> <span class="hljs-comment">// Check if request contains JSON data</span><br /> <span class="hljs-keyword">if</span> (request.is(<span class="hljs-string">'application/json'</span>)) {<br /> <span class="hljs-keyword">const</span> data = <span class="hljs-keyword">await</span> request.json();<br /> <span class="hljs-comment">// Handle JSON data</span><br /> }<br /> <br /> <span class="hljs-comment">// Check specific headers</span><br /> <span class="hljs-keyword">const</span> authHeader = request.get(<span class="hljs-string">'authorization'</span>);<br /> <span class="hljs-keyword">const</span> userAgent = request.get(<span class="hljs-string">'user-agent'</span>);<br /> <br /> <span class="hljs-comment">// Check request method</span><br /> <span class="hljs-keyword">if</span> (request.method === <span class="hljs-string">'POST'</span>) {<br /> <span class="hljs-comment">// Handle POST request</span><br /> }<br /> <br /> response.json({ success: <span class="hljs-literal">true</span> });<br />}</code></pre>
|
|
70
|
-
|
|
71
|
-
<h2>Response Examples</h2>
|
|
72
|
-
|
|
73
|
-
<h3>Different Response Types</h3>
|
|
74
|
-
<pre><code class="hljs javascript"><span class="hljs-comment">// Different response types</span><br /><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">request, response</span>) </span>{<br /> <span class="hljs-comment">// JSON response</span><br /> response.json({ message: <span class="hljs-string">'Hello World'</span> });<br /> <br /> <span class="hljs-comment">// HTML response</span><br /> response.html(<span class="hljs-string">'<h1>Hello World</h1>'</span>);<br /> <br /> <span class="hljs-comment">// Text response</span><br /> response.text(<span class="hljs-string">'Hello World'</span>);<br /> <br /> <span class="hljs-comment">// Status code chaining</span><br /> response.status(<span class="hljs-number">201</span>).json({ created: <span class="hljs-literal">true</span> });<br /> <br /> <span class="hljs-comment">// Custom headers</span><br /> response.set(<span class="hljs-string">'X-Custom-Header'</span>, <span class="hljs-string">'value'</span>).json({ data: <span class="hljs-string">'test'</span> });<br /> <br /> <span class="hljs-comment">// Redirect</span><br /> response.redirect(<span class="hljs-string">'/login'</span>);<br /> <br /> <span class="hljs-comment">// Set cookies</span><br /> response.cookie(<span class="hljs-string">'session'</span>, <span class="hljs-string">'abc123'</span>, { httpOnly: <span class="hljs-literal">true</span> }).json({ success: <span class="hljs-literal">true</span> });<br />}</code></pre>
|
|
75
|
-
|
|
76
|
-
<h3>Error Handling</h3>
|
|
77
|
-
<pre><code class="hljs javascript"><span class="hljs-comment">// api/user/[id]/DELETE.js</span><br /><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">request, response</span>) </span>{<br /> <span class="hljs-keyword">const</span> { id } = request.params;<br /> <br /> <span class="hljs-keyword">try</span> {<br /> <span class="hljs-keyword">const</span> user = <span class="hljs-keyword">await</span> getUserById(id);<br /> <br /> <span class="hljs-keyword">if</span> (!user) {<br /> <span class="hljs-keyword">return</span> response.status(<span class="hljs-number">404</span>).json({<br /> error: <span class="hljs-string">'User not found'</span>,<br /> code: <span class="hljs-string">'USER_NOT_FOUND'</span><br /> });<br /> }<br /> <br /> <span class="hljs-keyword">await</span> deleteUser(id);<br /> <br /> response.status(<span class="hljs-number">200</span>).json({<br /> message: <span class="hljs-string">'User deleted successfully'</span>,<br /> deletedAt: <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>().toISOString()<br /> });<br /> } <span class="hljs-keyword">catch</span> (error) {<br /> response.status(<span class="hljs-number">500</span>).json({<br /> error: <span class="hljs-string">'Internal server error'</span>,<br /> code: <span class="hljs-string">'INTERNAL_ERROR'</span><br /> });<br /> }<br />}</code></pre>
|
|
78
|
-
|
|
79
|
-
<h3>Content Negotiation</h3>
|
|
80
|
-
<pre><code class="hljs javascript"><span class="hljs-comment">// api/user/[id]/GET.js</span><br /><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">request, response</span>) </span>{<br /> <span class="hljs-keyword">const</span> { id } = request.params;<br /> <span class="hljs-keyword">const</span> userData = <span class="hljs-keyword">await</span> getUserById(id);<br /> <br /> <span class="hljs-comment">// Check Accept header to determine response format</span><br /> <span class="hljs-keyword">const</span> acceptHeader = request.get(<span class="hljs-string">'accept'</span>);<br /> <br /> <span class="hljs-keyword">if</span> (acceptHeader && acceptHeader.includes(<span class="hljs-string">'text/html'</span>)) {<br /> <span class="hljs-comment">// Return HTML representation</span><br /> response.html(<span class="hljs-string">`<br /> <div><br /> <h1>${userData.name}</h1><br /> <p>Email: ${userData.email}</p><br /> </div><br /> `</span>);<br /> } <span class="hljs-keyword">else</span> {<br /> <span class="hljs-comment">// Default to JSON</span><br /> response.json(userData);<br /> }<br />}</code></pre>
|
|
81
|
-
|
|
82
|
-
<h3>Working with Cookies</h3>
|
|
83
|
-
<pre><code class="hljs javascript"><span class="hljs-comment">// api/auth/login/POST.js</span><br /><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">request, response</span>) </span>{<br /> <span class="hljs-keyword">const</span> { username, password } = <span class="hljs-keyword">await</span> request.json();<br /> <br /> <span class="hljs-keyword">const</span> user = <span class="hljs-keyword">await</span> authenticateUser(username, password);<br /> <br /> <span class="hljs-keyword">if</span> (!user) {<br /> <span class="hljs-keyword">return</span> response.status(<span class="hljs-number">401</span>).json({ error: <span class="hljs-string">'Invalid credentials'</span> });<br /> }<br /> <br /> <span class="hljs-comment">// Set session cookie</span><br /> <span class="hljs-keyword">const</span> sessionToken = <span class="hljs-keyword">await</span> createSessionToken(user.id);<br /> <br /> response<br /> .cookie(<span class="hljs-string">'session'</span>, sessionToken, {<br /> httpOnly: <span class="hljs-literal">true</span>,<br /> secure: <span class="hljs-literal">true</span>,<br /> sameSite: <span class="hljs-string">'strict'</span>,<br /> maxAge: <span class="hljs-number">24</span> * <span class="hljs-number">60</span> * <span class="hljs-number">60</span> * <span class="hljs-number">1000</span> <span class="hljs-comment">// 24 hours</span><br /> })<br /> .json({<br /> message: <span class="hljs-string">'Login successful'</span>,<br /> user: {<br /> id: user.id,<br /> username: user.username,<br /> email: user.email<br /> }<br /> });<br />}</code></pre>
|
|
84
|
-
|
|
85
|
-
<h2>Advanced Request Handling</h2>
|
|
86
|
-
|
|
87
|
-
<h3>File Upload Handling</h3>
|
|
88
|
-
<pre><code class="hljs javascript"><span class="hljs-comment">// api/upload/POST.js</span><br /><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">request, response</span>) </span>{<br /> <span class="hljs-keyword">try</span> {<br /> <span class="hljs-comment">// Get raw buffer for file upload</span><br /> <span class="hljs-keyword">const</span> buffer = <span class="hljs-keyword">await</span> request.buffer();<br /> <span class="hljs-keyword">const</span> contentType = request.get(<span class="hljs-string">'content-type'</span>);<br /> <br /> <span class="hljs-comment">// Save file to disk</span><br /> <span class="hljs-keyword">const</span> filename = <span class="hljs-string">`upload_${Date.now()}.${getExtensionFromMimeType(contentType)}`</span>;<br /> <span class="hljs-keyword">await</span> fs.writeFile(<span class="hljs-string">`./uploads/${filename}`</span>, buffer);<br /> <br /> response.json({<br /> message: <span class="hljs-string">'File uploaded successfully'</span>,<br /> filename: filename,<br /> size: buffer.length<br /> });<br /> } <span class="hljs-keyword">catch</span> (error) {<br /> response.status(<span class="hljs-number">400</span>).json({ error: <span class="hljs-string">'Upload failed'</span> });<br /> }<br />}</code></pre>
|
|
89
|
-
|
|
90
|
-
<h3>Request Validation</h3>
|
|
91
|
-
<pre><code class="hljs javascript"><span class="hljs-comment">// api/user/POST.js</span><br /><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">request, response</span>) </span>{<br /> <span class="hljs-keyword">try</span> {<br /> <span class="hljs-keyword">const</span> userData = <span class="hljs-keyword">await</span> request.json();<br /> <br /> <span class="hljs-comment">// Validate required fields</span><br /> <span class="hljs-keyword">const</span> requiredFields = [<span class="hljs-string">'name'</span>, <span class="hljs-string">'email'</span>, <span class="hljs-string">'password'</span>];<br /> <span class="hljs-keyword">const</span> missingFields = requiredFields.filter(<span class="hljs-function"><span class="hljs-params">field</span> =></span> !userData[field]);<br /> <br /> <span class="hljs-keyword">if</span> (missingFields.length > <span class="hljs-number">0</span>) {<br /> <span class="hljs-keyword">return</span> response.status(<span class="hljs-number">400</span>).json({<br /> error: <span class="hljs-string">'Missing required fields'</span>,<br /> missingFields<br /> });<br /> }<br /> <br /> <span class="hljs-comment">// Validate email format</span><br /> <span class="hljs-keyword">const</span> emailRegex = <span class="hljs-regexp">/^[^\s@]+@[^\s@]+\.[^\s@]+$/</span>;<br /> <span class="hljs-keyword">if</span> (!emailRegex.test(userData.email)) {<br /> <span class="hljs-keyword">return</span> response.status(<span class="hljs-number">400</span>).json({<br /> error: <span class="hljs-string">'Invalid email format'</span><br /> });<br /> }<br /> <br /> <span class="hljs-comment">// Create user</span><br /> <span class="hljs-keyword">const</span> newUser = <span class="hljs-keyword">await</span> createUser(userData);<br /> <br /> response.status(<span class="hljs-number">201</span>).json({<br /> message: <span class="hljs-string">'User created successfully'</span>,<br /> user: {<br /> id: newUser.id,<br /> name: newUser.name,<br /> email: newUser.email<br /> }<br /> });<br /> } <span class="hljs-keyword">catch</span> (error) {<br /> response.status(<span class="hljs-number">400</span>).json({ error: <span class="hljs-string">'Invalid request data'</span> });<br /> }<br />}</code></pre>
|
|
92
|
-
</main>
|
|
93
|
-
<div style="height:25vh"></div><div style="height:25vh"></div>
|
|
94
|
-
</body>
|
|
95
|
-
</html>
|
|
1
|
+
<html lang="en" theme="auto">
|
|
2
|
+
<head>
|
|
3
|
+
<meta charset='utf-8'>
|
|
4
|
+
<meta http-equiv='X-UA-Compatible' content='IE=edge'>
|
|
5
|
+
<title>Request & Response - Kempo Server</title>
|
|
6
|
+
<meta name='viewport' content='width=device-width, initial-scale=1'>
|
|
7
|
+
<link rel="icon" type="image/png" sizes="48x48" href="media/icon48.png">
|
|
8
|
+
<link rel="manifest" href="manifest.json">
|
|
9
|
+
<link rel="stylesheet" href="essential.css" />
|
|
10
|
+
</head>
|
|
11
|
+
<body>
|
|
12
|
+
<main>
|
|
13
|
+
<a href="./" class="btn">Home</a>
|
|
14
|
+
<h1>Request & Response Objects</h1>
|
|
15
|
+
<p>Learn how to work with HTTP requests and responses in Kempo Server.</p>
|
|
16
|
+
|
|
17
|
+
<h2>Request Object</h2>
|
|
18
|
+
<p>Kempo Server provides a request object that makes working with HTTP requests easier:</p>
|
|
19
|
+
|
|
20
|
+
<h3>Properties</h3>
|
|
21
|
+
<ul>
|
|
22
|
+
<li><code>request.params</code> - Route parameters from dynamic routes (e.g., <code>{ id: "123", info: "info" }</code>)</li>
|
|
23
|
+
<li><code>request.query</code> - Query string parameters as an object (e.g., <code>{ page: "1", limit: "10" }</code>)</li>
|
|
24
|
+
<li><code>request.path</code> - The pathname of the request URL</li>
|
|
25
|
+
<li><code>request.method</code> - HTTP method (GET, POST, etc.)</li>
|
|
26
|
+
<li><code>request.headers</code> - Request headers object</li>
|
|
27
|
+
<li><code>request.url</code> - Full request URL</li>
|
|
28
|
+
</ul>
|
|
29
|
+
|
|
30
|
+
<h3>Methods</h3>
|
|
31
|
+
<ul>
|
|
32
|
+
<li><code>await request.json()</code> - Parse request body as JSON</li>
|
|
33
|
+
<li><code>await request.text()</code> - Get request body as text</li>
|
|
34
|
+
<li><code>await request.body()</code> - Get raw request body as string</li>
|
|
35
|
+
<li><code>await request.buffer()</code> - Get request body as Buffer</li>
|
|
36
|
+
<li><code>request.get(headerName)</code> - Get specific header value</li>
|
|
37
|
+
<li><code>request.is(type)</code> - Check if content-type contains specified type</li>
|
|
38
|
+
</ul>
|
|
39
|
+
|
|
40
|
+
<h2>Response Object</h2>
|
|
41
|
+
<p>Kempo Server also provides a response object that makes sending responses easier:</p>
|
|
42
|
+
|
|
43
|
+
<h3>Methods</h3>
|
|
44
|
+
<ul>
|
|
45
|
+
<li><code>response.json(object)</code> - Send JSON response with automatic content-type</li>
|
|
46
|
+
<li><code>response.send(data)</code> - Send response (auto-detects content type)</li>
|
|
47
|
+
<li><code>response.html(htmlString)</code> - Send HTML response</li>
|
|
48
|
+
<li><code>response.text(textString)</code> - Send plain text response</li>
|
|
49
|
+
<li><code>response.status(code)</code> - Set status code (chainable)</li>
|
|
50
|
+
<li><code>response.set(field, value)</code> - Set header (chainable)</li>
|
|
51
|
+
<li><code>response.type(contentType)</code> - Set content type (chainable)</li>
|
|
52
|
+
<li><code>response.redirect(url, statusCode)</code> - Redirect to URL</li>
|
|
53
|
+
<li><code>response.cookie(name, value, options)</code> - Set cookie</li>
|
|
54
|
+
<li><code>response.clearCookie(name, options)</code> - Clear cookie</li>
|
|
55
|
+
</ul>
|
|
56
|
+
|
|
57
|
+
<h2>Request Examples</h2>
|
|
58
|
+
|
|
59
|
+
<h3>Accessing Route Parameters</h3>
|
|
60
|
+
<pre><code class="hljs javascript"><span class="hljs-comment">// api/user/[id]/GET.js</span><br /><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">request, response</span>) </span>{<br /> <span class="hljs-keyword">const</span> { id } = request.params;<br /> <span class="hljs-keyword">const</span> { includeDetails } = request.query;<br /> <br /> <span class="hljs-comment">// Fetch user data from database</span><br /> <span class="hljs-keyword">const</span> userData = <span class="hljs-keyword">await</span> getUserById(id);<br /> <br /> <span class="hljs-keyword">if</span> (!userData) {<br /> <span class="hljs-keyword">return</span> response.status(<span class="hljs-number">404</span>).json({ error: <span class="hljs-string">'User not found'</span> });<br /> }<br /> <br /> response.json(userData);<br />}</code></pre>
|
|
61
|
+
|
|
62
|
+
<h3>Handling JSON Data</h3>
|
|
63
|
+
<pre><code class="hljs javascript"><span class="hljs-comment">// api/user/[id]/POST.js</span><br /><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">request, response</span>) </span>{<br /> <span class="hljs-keyword">const</span> { id } = request.params;<br /> <br /> <span class="hljs-keyword">try</span> {<br /> <span class="hljs-keyword">const</span> updateData = <span class="hljs-keyword">await</span> request.json();<br /> <br /> <span class="hljs-comment">// Update user in database</span><br /> <span class="hljs-keyword">const</span> updatedUser = <span class="hljs-keyword">await</span> updateUser(id, updateData);<br /> <br /> response.json(updatedUser);<br /> } <span class="hljs-keyword">catch</span> (error) {<br /> response.status(<span class="hljs-number">400</span>).json({ error: <span class="hljs-string">'Invalid JSON'</span> });<br /> }<br />}</code></pre>
|
|
64
|
+
|
|
65
|
+
<h3>Working with Form Data</h3>
|
|
66
|
+
<pre><code class="hljs javascript"><span class="hljs-comment">// contact/POST.js</span><br /><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">request, response</span>) </span>{<br /> <span class="hljs-keyword">try</span> {<br /> <span class="hljs-keyword">const</span> body = <span class="hljs-keyword">await</span> request.text();<br /> <span class="hljs-keyword">const</span> formData = <span class="hljs-keyword">new</span> URLSearchParams(body);<br /> <span class="hljs-keyword">const</span> name = formData.get(<span class="hljs-string">'name'</span>);<br /> <span class="hljs-keyword">const</span> email = formData.get(<span class="hljs-string">'email'</span>);<br /> <span class="hljs-keyword">const</span> message = formData.get(<span class="hljs-string">'message'</span>);<br /> <br /> <span class="hljs-comment">// Process form data...</span><br /> <span class="hljs-keyword">await</span> sendContactEmail({ name, email, message });<br /> <br /> response.html(<span class="hljs-string">'<h1>Thank you for your message!</h1>'</span>);<br /> } <span class="hljs-keyword">catch</span> (error) {<br /> response.status(<span class="hljs-number">400</span>).html(<span class="hljs-string">'<h1>Error processing form</h1>'</span>);<br /> }<br />}</code></pre>
|
|
67
|
+
|
|
68
|
+
<h3>Checking Headers</h3>
|
|
69
|
+
<pre><code class="hljs javascript"><span class="hljs-comment">// api/upload/POST.js</span><br /><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">request, response</span>) </span>{<br /> <span class="hljs-comment">// Check if request contains JSON data</span><br /> <span class="hljs-keyword">if</span> (request.is(<span class="hljs-string">'application/json'</span>)) {<br /> <span class="hljs-keyword">const</span> data = <span class="hljs-keyword">await</span> request.json();<br /> <span class="hljs-comment">// Handle JSON data</span><br /> }<br /> <br /> <span class="hljs-comment">// Check specific headers</span><br /> <span class="hljs-keyword">const</span> authHeader = request.get(<span class="hljs-string">'authorization'</span>);<br /> <span class="hljs-keyword">const</span> userAgent = request.get(<span class="hljs-string">'user-agent'</span>);<br /> <br /> <span class="hljs-comment">// Check request method</span><br /> <span class="hljs-keyword">if</span> (request.method === <span class="hljs-string">'POST'</span>) {<br /> <span class="hljs-comment">// Handle POST request</span><br /> }<br /> <br /> response.json({ success: <span class="hljs-literal">true</span> });<br />}</code></pre>
|
|
70
|
+
|
|
71
|
+
<h2>Response Examples</h2>
|
|
72
|
+
|
|
73
|
+
<h3>Different Response Types</h3>
|
|
74
|
+
<pre><code class="hljs javascript"><span class="hljs-comment">// Different response types</span><br /><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">request, response</span>) </span>{<br /> <span class="hljs-comment">// JSON response</span><br /> response.json({ message: <span class="hljs-string">'Hello World'</span> });<br /> <br /> <span class="hljs-comment">// HTML response</span><br /> response.html(<span class="hljs-string">'<h1>Hello World</h1>'</span>);<br /> <br /> <span class="hljs-comment">// Text response</span><br /> response.text(<span class="hljs-string">'Hello World'</span>);<br /> <br /> <span class="hljs-comment">// Status code chaining</span><br /> response.status(<span class="hljs-number">201</span>).json({ created: <span class="hljs-literal">true</span> });<br /> <br /> <span class="hljs-comment">// Custom headers</span><br /> response.set(<span class="hljs-string">'X-Custom-Header'</span>, <span class="hljs-string">'value'</span>).json({ data: <span class="hljs-string">'test'</span> });<br /> <br /> <span class="hljs-comment">// Redirect</span><br /> response.redirect(<span class="hljs-string">'/login'</span>);<br /> <br /> <span class="hljs-comment">// Set cookies</span><br /> response.cookie(<span class="hljs-string">'session'</span>, <span class="hljs-string">'abc123'</span>, { httpOnly: <span class="hljs-literal">true</span> }).json({ success: <span class="hljs-literal">true</span> });<br />}</code></pre>
|
|
75
|
+
|
|
76
|
+
<h3>Error Handling</h3>
|
|
77
|
+
<pre><code class="hljs javascript"><span class="hljs-comment">// api/user/[id]/DELETE.js</span><br /><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">request, response</span>) </span>{<br /> <span class="hljs-keyword">const</span> { id } = request.params;<br /> <br /> <span class="hljs-keyword">try</span> {<br /> <span class="hljs-keyword">const</span> user = <span class="hljs-keyword">await</span> getUserById(id);<br /> <br /> <span class="hljs-keyword">if</span> (!user) {<br /> <span class="hljs-keyword">return</span> response.status(<span class="hljs-number">404</span>).json({<br /> error: <span class="hljs-string">'User not found'</span>,<br /> code: <span class="hljs-string">'USER_NOT_FOUND'</span><br /> });<br /> }<br /> <br /> <span class="hljs-keyword">await</span> deleteUser(id);<br /> <br /> response.status(<span class="hljs-number">200</span>).json({<br /> message: <span class="hljs-string">'User deleted successfully'</span>,<br /> deletedAt: <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>().toISOString()<br /> });<br /> } <span class="hljs-keyword">catch</span> (error) {<br /> response.status(<span class="hljs-number">500</span>).json({<br /> error: <span class="hljs-string">'Internal server error'</span>,<br /> code: <span class="hljs-string">'INTERNAL_ERROR'</span><br /> });<br /> }<br />}</code></pre>
|
|
78
|
+
|
|
79
|
+
<h3>Content Negotiation</h3>
|
|
80
|
+
<pre><code class="hljs javascript"><span class="hljs-comment">// api/user/[id]/GET.js</span><br /><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">request, response</span>) </span>{<br /> <span class="hljs-keyword">const</span> { id } = request.params;<br /> <span class="hljs-keyword">const</span> userData = <span class="hljs-keyword">await</span> getUserById(id);<br /> <br /> <span class="hljs-comment">// Check Accept header to determine response format</span><br /> <span class="hljs-keyword">const</span> acceptHeader = request.get(<span class="hljs-string">'accept'</span>);<br /> <br /> <span class="hljs-keyword">if</span> (acceptHeader && acceptHeader.includes(<span class="hljs-string">'text/html'</span>)) {<br /> <span class="hljs-comment">// Return HTML representation</span><br /> response.html(<span class="hljs-string">`<br /> <div><br /> <h1>${userData.name}</h1><br /> <p>Email: ${userData.email}</p><br /> </div><br /> `</span>);<br /> } <span class="hljs-keyword">else</span> {<br /> <span class="hljs-comment">// Default to JSON</span><br /> response.json(userData);<br /> }<br />}</code></pre>
|
|
81
|
+
|
|
82
|
+
<h3>Working with Cookies</h3>
|
|
83
|
+
<pre><code class="hljs javascript"><span class="hljs-comment">// api/auth/login/POST.js</span><br /><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">request, response</span>) </span>{<br /> <span class="hljs-keyword">const</span> { username, password } = <span class="hljs-keyword">await</span> request.json();<br /> <br /> <span class="hljs-keyword">const</span> user = <span class="hljs-keyword">await</span> authenticateUser(username, password);<br /> <br /> <span class="hljs-keyword">if</span> (!user) {<br /> <span class="hljs-keyword">return</span> response.status(<span class="hljs-number">401</span>).json({ error: <span class="hljs-string">'Invalid credentials'</span> });<br /> }<br /> <br /> <span class="hljs-comment">// Set session cookie</span><br /> <span class="hljs-keyword">const</span> sessionToken = <span class="hljs-keyword">await</span> createSessionToken(user.id);<br /> <br /> response<br /> .cookie(<span class="hljs-string">'session'</span>, sessionToken, {<br /> httpOnly: <span class="hljs-literal">true</span>,<br /> secure: <span class="hljs-literal">true</span>,<br /> sameSite: <span class="hljs-string">'strict'</span>,<br /> maxAge: <span class="hljs-number">24</span> * <span class="hljs-number">60</span> * <span class="hljs-number">60</span> * <span class="hljs-number">1000</span> <span class="hljs-comment">// 24 hours</span><br /> })<br /> .json({<br /> message: <span class="hljs-string">'Login successful'</span>,<br /> user: {<br /> id: user.id,<br /> username: user.username,<br /> email: user.email<br /> }<br /> });<br />}</code></pre>
|
|
84
|
+
|
|
85
|
+
<h2>Advanced Request Handling</h2>
|
|
86
|
+
|
|
87
|
+
<h3>File Upload Handling</h3>
|
|
88
|
+
<pre><code class="hljs javascript"><span class="hljs-comment">// api/upload/POST.js</span><br /><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">request, response</span>) </span>{<br /> <span class="hljs-keyword">try</span> {<br /> <span class="hljs-comment">// Get raw buffer for file upload</span><br /> <span class="hljs-keyword">const</span> buffer = <span class="hljs-keyword">await</span> request.buffer();<br /> <span class="hljs-keyword">const</span> contentType = request.get(<span class="hljs-string">'content-type'</span>);<br /> <br /> <span class="hljs-comment">// Save file to disk</span><br /> <span class="hljs-keyword">const</span> filename = <span class="hljs-string">`upload_${Date.now()}.${getExtensionFromMimeType(contentType)}`</span>;<br /> <span class="hljs-keyword">await</span> fs.writeFile(<span class="hljs-string">`./uploads/${filename}`</span>, buffer);<br /> <br /> response.json({<br /> message: <span class="hljs-string">'File uploaded successfully'</span>,<br /> filename: filename,<br /> size: buffer.length<br /> });<br /> } <span class="hljs-keyword">catch</span> (error) {<br /> response.status(<span class="hljs-number">400</span>).json({ error: <span class="hljs-string">'Upload failed'</span> });<br /> }<br />}</code></pre>
|
|
89
|
+
|
|
90
|
+
<h3>Request Validation</h3>
|
|
91
|
+
<pre><code class="hljs javascript"><span class="hljs-comment">// api/user/POST.js</span><br /><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">request, response</span>) </span>{<br /> <span class="hljs-keyword">try</span> {<br /> <span class="hljs-keyword">const</span> userData = <span class="hljs-keyword">await</span> request.json();<br /> <br /> <span class="hljs-comment">// Validate required fields</span><br /> <span class="hljs-keyword">const</span> requiredFields = [<span class="hljs-string">'name'</span>, <span class="hljs-string">'email'</span>, <span class="hljs-string">'password'</span>];<br /> <span class="hljs-keyword">const</span> missingFields = requiredFields.filter(<span class="hljs-function"><span class="hljs-params">field</span> =></span> !userData[field]);<br /> <br /> <span class="hljs-keyword">if</span> (missingFields.length > <span class="hljs-number">0</span>) {<br /> <span class="hljs-keyword">return</span> response.status(<span class="hljs-number">400</span>).json({<br /> error: <span class="hljs-string">'Missing required fields'</span>,<br /> missingFields<br /> });<br /> }<br /> <br /> <span class="hljs-comment">// Validate email format</span><br /> <span class="hljs-keyword">const</span> emailRegex = <span class="hljs-regexp">/^[^\s@]+@[^\s@]+\.[^\s@]+$/</span>;<br /> <span class="hljs-keyword">if</span> (!emailRegex.test(userData.email)) {<br /> <span class="hljs-keyword">return</span> response.status(<span class="hljs-number">400</span>).json({<br /> error: <span class="hljs-string">'Invalid email format'</span><br /> });<br /> }<br /> <br /> <span class="hljs-comment">// Create user</span><br /> <span class="hljs-keyword">const</span> newUser = <span class="hljs-keyword">await</span> createUser(userData);<br /> <br /> response.status(<span class="hljs-number">201</span>).json({<br /> message: <span class="hljs-string">'User created successfully'</span>,<br /> user: {<br /> id: newUser.id,<br /> name: newUser.name,<br /> email: newUser.email<br /> }<br /> });<br /> } <span class="hljs-keyword">catch</span> (error) {<br /> response.status(<span class="hljs-number">400</span>).json({ error: <span class="hljs-string">'Invalid request data'</span> });<br /> }<br />}</code></pre>
|
|
92
|
+
</main>
|
|
93
|
+
<div style="height:25vh"></div><div style="height:25vh"></div>
|
|
94
|
+
</body>
|
|
95
|
+
</html>
|
package/docs/routing.html
CHANGED
|
@@ -1,77 +1,77 @@
|
|
|
1
|
-
<html lang="en" theme="auto">
|
|
2
|
-
<head>
|
|
3
|
-
<meta charset='utf-8'>
|
|
4
|
-
<meta http-equiv='X-UA-Compatible' content='IE=edge'>
|
|
5
|
-
<title>Routes & Routing - Kempo Server</title>
|
|
6
|
-
<meta name='viewport' content='width=device-width, initial-scale=1'>
|
|
7
|
-
<link rel="icon" type="image/png" sizes="48x48" href="media/icon48.png">
|
|
8
|
-
<link rel="manifest" href="manifest.json">
|
|
9
|
-
<link rel="stylesheet" href="essential.css" />
|
|
10
|
-
</head>
|
|
11
|
-
<body>
|
|
12
|
-
<main>
|
|
13
|
-
<a href="./" class="btn">Home</a>
|
|
14
|
-
<h1>Routes & Routing</h1>
|
|
15
|
-
<p>Learn how Kempo Server's file-based routing system works.</p>
|
|
16
|
-
|
|
17
|
-
<h2>How Routes Work</h2>
|
|
18
|
-
<p>A route is a request to a directory that will be handled by a file. To define routes, create the directory structure to the route and create a file with the name of the method that this file will handle. For example <code>GET.js</code> will handle the <code>GET</code> requests, <code>POST.js</code> will handle the <code>POST</code> requests and so on. Use <code>index.js</code> to handle all request types.</p>
|
|
19
|
-
|
|
20
|
-
<p>The Javascript file must have a <b>default</b> export that is the function that will handle the request. It will be passed a <code>request</code> object and a <code>response</code> object.</p>
|
|
21
|
-
|
|
22
|
-
<p>For example this directory structure:</p>
|
|
23
|
-
<pre><code class="hljs markdown">my/<br />├─ route/<br />│ ├─ GET.js<br />│ ├─ POST.js<br />├─ other/<br />│ ├─ route/<br />│ │ ├─ GET.js<br /></code></pre>
|
|
24
|
-
<p>Would be used to handle <code>GET my/route/</code>, <code>POST my/route/</code> and <code>GET my/other/route/</code></p>
|
|
25
|
-
|
|
26
|
-
<h2 id="htmlRoutes">HTML Routes</h2>
|
|
27
|
-
<p>Just like JS files, HTML files can be used to define a route. Use <code>GET.html</code>, <code>POST.html</code>, etc... to define files that will be served when that route is requested.</p>
|
|
28
|
-
<pre><code class="hljs markdown">my/<br />├─ route/<br />│ ├─ GET.js<br />│ ├─ POST.js<br />├─ other/<br />│ ├─ route/<br />│ │ ├─ GET.js<br />│ ├─ POST.html<br />│ ├─ GET.html<br /></code></pre>
|
|
29
|
-
|
|
30
|
-
<h3><code>index</code> fallbacks</h3>
|
|
31
|
-
<p><code>index.js</code> or <code>index.html</code> will be used as a fallback for all routes if a <i>method</i> file is not defined. In the above examples we do not have any routes defined for <code>DELETE</code>, <code>PUT</code> <code>PATCH</code>, etc... so lets use an <code>index.js</code> and <code>index.html</code> to be a "catch-all" for all the methods we have not created handlers for.</p>
|
|
32
|
-
<pre><code class="hljs markdown">my/<br />├─ route/<br />│ ├─ GET.js<br />│ ├─ POST.js<br />│ ├─ index.js<br />├─ other/<br />│ ├─ route/<br />│ │ ├─ GET.js<br />│ │ ├─ index.js<br />│ ├─ POST.html<br />│ ├─ GET.html<br />│ ├─ index.html<br />├─ index.html<br /></code></pre>
|
|
33
|
-
|
|
34
|
-
<h2>Dynamic Routes</h2>
|
|
35
|
-
<p>A dynamic route is a route with a "param" in its path. To define the dynamic parts of the route just wrap the directory name in square brackets. For example if you wanted to get a users profile, or perform CRUD operations on a user you might create the following directory structure.</p>
|
|
36
|
-
<pre><code class="hljs markdown">api/<br />├─ user/<br />│ ├─ [id]/<br />│ │ ├─ [info]/<br />│ │ │ ├─ GET.js<br />│ │ │ ├─ DELETE.js<br />│ │ │ ├─ PUT.js<br />│ │ │ ├─ POST.js<br />│ │ ├─ GET.js<br /></code></pre>
|
|
37
|
-
<p>When a request is made to <code>/api/user/123/info</code>, the route file <code>api/user/[id]/[info]/GET.js</code> would be executed and receive a request object with <code>request.params</code> containing <code>{ id: "123", info: "info" }</code>.</p>
|
|
38
|
-
|
|
39
|
-
<h2>Route Examples</h2>
|
|
40
|
-
|
|
41
|
-
<h3>Simple API Route</h3>
|
|
42
|
-
<pre><code class="hljs javascript"><span class="hljs-comment">// api/hello/GET.js</span><br /><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">request, response</span>) </span>{<br /> <span class="hljs-keyword">const</span> { name } = request.query;<br /> <span class="hljs-keyword">const</span> message = name ? <span class="hljs-string">`Hello ${name}!`</span> : <span class="hljs-string">'Hello World!'</span>;<br /> <br /> response.json({ message });<br />}</code></pre>
|
|
43
|
-
|
|
44
|
-
<h3>Dynamic User Profile Route</h3>
|
|
45
|
-
<pre><code class="hljs javascript"><span class="hljs-comment">// api/users/[id]/GET.js</span><br /><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">request, response</span>) </span>{<br /> <span class="hljs-keyword">const</span> { id } = request.params;<br /> <span class="hljs-keyword">const</span> { includeProfile } = request.query;<br /> <br /> <span class="hljs-comment">// Simulate database lookup</span><br /> <span class="hljs-keyword">const</span> user = {<br /> id: id,<br /> name: <span class="hljs-string">`User ${id}`</span>,<br /> email: <span class="hljs-string">`user${id}@example.com`</span><br /> };<br /> <br /> <span class="hljs-keyword">if</span> (includeProfile === <span class="hljs-string">'true'</span>) {<br /> user.profile = {<br /> bio: <span class="hljs-string">`Bio for user ${id}`</span>,<br /> joinDate: <span class="hljs-string">'2024-01-01'</span><br /> };<br /> }<br /> <br /> response.json(user);<br />}</code></pre>
|
|
46
|
-
|
|
47
|
-
<h3>Nested Dynamic Routes</h3>
|
|
48
|
-
<pre><code class="hljs javascript"><span class="hljs-comment">// api/users/[id]/posts/[postId]/GET.js</span><br /><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">request, response</span>) </span>{<br /> <span class="hljs-keyword">const</span> { id, postId } = request.params;<br /> <br /> <span class="hljs-keyword">const</span> post = {<br /> id: postId,<br /> userId: id,<br /> title: <span class="hljs-string">`Post ${postId} by User ${id}`</span>,<br /> content: <span class="hljs-string">'This is the post content...'</span>,<br /> createdAt: <span class="hljs-string">'2024-01-01T00:00:00.000Z'</span><br /> };<br /> <br /> response.json(post);<br />}</code></pre>
|
|
49
|
-
|
|
50
|
-
<h3>HTML Route Example</h3>
|
|
51
|
-
<pre><code class="hljs html"><span class="hljs-comment"><!-- pages/about/GET.html --></span><br /><span class="hljs-meta"><!DOCTYPE html></span><br /><span class="hljs-tag"><<span class="hljs-name">html</span>></span><br /><span class="hljs-tag"><<span class="hljs-name">head</span>></span><br /> <span class="hljs-tag"><<span class="hljs-name">title</span>></span>About Us<span class="hljs-tag"></<span class="hljs-name">title</span>></span><br /><span class="hljs-tag"></<span class="hljs-name">head</span>></span><br /><span class="hljs-tag"><<span class="hljs-name">body</span>></span><br /> <span class="hljs-tag"><<span class="hljs-name">h1</span>></span>About Our Company<span class="hljs-tag"></<span class="hljs-name">h1</span>></span><br /> <span class="hljs-tag"><<span class="hljs-name">p</span>></span>We are a company that does amazing things.<span class="hljs-tag"></<span class="hljs-name">p</span>></span><br /><span class="hljs-tag"></<span class="hljs-name">body</span>></span><br /><span class="hljs-tag"></<span class="hljs-name">html</span>></span></code></pre>
|
|
52
|
-
|
|
53
|
-
<h2>Route File Structure Best Practices</h2>
|
|
54
|
-
|
|
55
|
-
<h3>Organize by Feature</h3>
|
|
56
|
-
<pre><code class="hljs markdown">api/<br />├─ auth/<br />│ ├─ login/<br />│ │ ├─ POST.js<br />│ ├─ logout/<br />│ │ ├─ POST.js<br />│ ├─ register/<br />│ │ ├─ POST.js<br />├─ users/<br />│ ├─ [id]/<br />│ │ ├─ GET.js<br />│ │ ├─ PUT.js<br />│ │ ├─ DELETE.js<br />│ ├─ GET.js<br />│ ├─ POST.js<br />├─ posts/<br />│ ├─ [id]/<br />│ │ ├─ GET.js<br />│ │ ├─ PUT.js<br />│ │ ├─ DELETE.js<br />│ ├─ GET.js<br />│ ├─ POST.js<br /></code></pre>
|
|
57
|
-
|
|
58
|
-
<h3>Use Index Files for Fallbacks</h3>
|
|
59
|
-
<p>Use <code>index.js</code> to handle methods not explicitly defined:</p>
|
|
60
|
-
<pre><code class="hljs javascript"><span class="hljs-comment">// api/users/index.js</span><br /><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">request, response</span>) </span>{<br /> <span class="hljs-comment">// Handle any method not explicitly defined</span><br /> response.status(<span class="hljs-number">405</span>).json({<br /> error: <span class="hljs-string">'Method not allowed'</span>,<br /> allowed: [<span class="hljs-string">'GET'</span>, <span class="hljs-string">'POST'</span>]<br /> });<br />}</code></pre>
|
|
61
|
-
|
|
62
|
-
<h2>Static File Serving</h2>
|
|
63
|
-
<p>Any file that doesn't match a route pattern will be served as a static file. This includes:</p>
|
|
64
|
-
<ul>
|
|
65
|
-
<li>HTML files (except route files)</li>
|
|
66
|
-
<li>CSS files</li>
|
|
67
|
-
<li>JavaScript files (except route files)</li>
|
|
68
|
-
<li>Images</li>
|
|
69
|
-
<li>Any other static assets</li>
|
|
70
|
-
</ul>
|
|
71
|
-
|
|
72
|
-
<p>Example static file structure:</p>
|
|
73
|
-
<pre><code class="hljs markdown">public/<br />├─ index.html # Served at /<br />├─ styles.css # Served at /styles.css<br />├─ script.js # Served at /script.js<br />├─ images/<br />│ ├─ logo.png # Served at /images/logo.png<br />├─ api/ # Routes directory<br />│ ├─ hello/GET.js # Route handler<br /></code></pre>
|
|
74
|
-
</main>
|
|
75
|
-
<div style="height:25vh"></div>
|
|
76
|
-
</body>
|
|
77
|
-
</html>
|
|
1
|
+
<html lang="en" theme="auto">
|
|
2
|
+
<head>
|
|
3
|
+
<meta charset='utf-8'>
|
|
4
|
+
<meta http-equiv='X-UA-Compatible' content='IE=edge'>
|
|
5
|
+
<title>Routes & Routing - Kempo Server</title>
|
|
6
|
+
<meta name='viewport' content='width=device-width, initial-scale=1'>
|
|
7
|
+
<link rel="icon" type="image/png" sizes="48x48" href="media/icon48.png">
|
|
8
|
+
<link rel="manifest" href="manifest.json">
|
|
9
|
+
<link rel="stylesheet" href="essential.css" />
|
|
10
|
+
</head>
|
|
11
|
+
<body>
|
|
12
|
+
<main>
|
|
13
|
+
<a href="./" class="btn">Home</a>
|
|
14
|
+
<h1>Routes & Routing</h1>
|
|
15
|
+
<p>Learn how Kempo Server's file-based routing system works.</p>
|
|
16
|
+
|
|
17
|
+
<h2>How Routes Work</h2>
|
|
18
|
+
<p>A route is a request to a directory that will be handled by a file. To define routes, create the directory structure to the route and create a file with the name of the method that this file will handle. For example <code>GET.js</code> will handle the <code>GET</code> requests, <code>POST.js</code> will handle the <code>POST</code> requests and so on. Use <code>index.js</code> to handle all request types.</p>
|
|
19
|
+
|
|
20
|
+
<p>The Javascript file must have a <b>default</b> export that is the function that will handle the request. It will be passed a <code>request</code> object and a <code>response</code> object.</p>
|
|
21
|
+
|
|
22
|
+
<p>For example this directory structure:</p>
|
|
23
|
+
<pre><code class="hljs markdown">my/<br />├─ route/<br />│ ├─ GET.js<br />│ ├─ POST.js<br />├─ other/<br />│ ├─ route/<br />│ │ ├─ GET.js<br /></code></pre>
|
|
24
|
+
<p>Would be used to handle <code>GET my/route/</code>, <code>POST my/route/</code> and <code>GET my/other/route/</code></p>
|
|
25
|
+
|
|
26
|
+
<h2 id="htmlRoutes">HTML Routes</h2>
|
|
27
|
+
<p>Just like JS files, HTML files can be used to define a route. Use <code>GET.html</code>, <code>POST.html</code>, etc... to define files that will be served when that route is requested.</p>
|
|
28
|
+
<pre><code class="hljs markdown">my/<br />├─ route/<br />│ ├─ GET.js<br />│ ├─ POST.js<br />├─ other/<br />│ ├─ route/<br />│ │ ├─ GET.js<br />│ ├─ POST.html<br />│ ├─ GET.html<br /></code></pre>
|
|
29
|
+
|
|
30
|
+
<h3><code>index</code> fallbacks</h3>
|
|
31
|
+
<p><code>index.js</code> or <code>index.html</code> will be used as a fallback for all routes if a <i>method</i> file is not defined. In the above examples we do not have any routes defined for <code>DELETE</code>, <code>PUT</code> <code>PATCH</code>, etc... so lets use an <code>index.js</code> and <code>index.html</code> to be a "catch-all" for all the methods we have not created handlers for.</p>
|
|
32
|
+
<pre><code class="hljs markdown">my/<br />├─ route/<br />│ ├─ GET.js<br />│ ├─ POST.js<br />│ ├─ index.js<br />├─ other/<br />│ ├─ route/<br />│ │ ├─ GET.js<br />│ │ ├─ index.js<br />│ ├─ POST.html<br />│ ├─ GET.html<br />│ ├─ index.html<br />├─ index.html<br /></code></pre>
|
|
33
|
+
|
|
34
|
+
<h2>Dynamic Routes</h2>
|
|
35
|
+
<p>A dynamic route is a route with a "param" in its path. To define the dynamic parts of the route just wrap the directory name in square brackets. For example if you wanted to get a users profile, or perform CRUD operations on a user you might create the following directory structure.</p>
|
|
36
|
+
<pre><code class="hljs markdown">api/<br />├─ user/<br />│ ├─ [id]/<br />│ │ ├─ [info]/<br />│ │ │ ├─ GET.js<br />│ │ │ ├─ DELETE.js<br />│ │ │ ├─ PUT.js<br />│ │ │ ├─ POST.js<br />│ │ ├─ GET.js<br /></code></pre>
|
|
37
|
+
<p>When a request is made to <code>/api/user/123/info</code>, the route file <code>api/user/[id]/[info]/GET.js</code> would be executed and receive a request object with <code>request.params</code> containing <code>{ id: "123", info: "info" }</code>.</p>
|
|
38
|
+
|
|
39
|
+
<h2>Route Examples</h2>
|
|
40
|
+
|
|
41
|
+
<h3>Simple API Route</h3>
|
|
42
|
+
<pre><code class="hljs javascript"><span class="hljs-comment">// api/hello/GET.js</span><br /><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">request, response</span>) </span>{<br /> <span class="hljs-keyword">const</span> { name } = request.query;<br /> <span class="hljs-keyword">const</span> message = name ? <span class="hljs-string">`Hello ${name}!`</span> : <span class="hljs-string">'Hello World!'</span>;<br /> <br /> response.json({ message });<br />}</code></pre>
|
|
43
|
+
|
|
44
|
+
<h3>Dynamic User Profile Route</h3>
|
|
45
|
+
<pre><code class="hljs javascript"><span class="hljs-comment">// api/users/[id]/GET.js</span><br /><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">request, response</span>) </span>{<br /> <span class="hljs-keyword">const</span> { id } = request.params;<br /> <span class="hljs-keyword">const</span> { includeProfile } = request.query;<br /> <br /> <span class="hljs-comment">// Simulate database lookup</span><br /> <span class="hljs-keyword">const</span> user = {<br /> id: id,<br /> name: <span class="hljs-string">`User ${id}`</span>,<br /> email: <span class="hljs-string">`user${id}@example.com`</span><br /> };<br /> <br /> <span class="hljs-keyword">if</span> (includeProfile === <span class="hljs-string">'true'</span>) {<br /> user.profile = {<br /> bio: <span class="hljs-string">`Bio for user ${id}`</span>,<br /> joinDate: <span class="hljs-string">'2024-01-01'</span><br /> };<br /> }<br /> <br /> response.json(user);<br />}</code></pre>
|
|
46
|
+
|
|
47
|
+
<h3>Nested Dynamic Routes</h3>
|
|
48
|
+
<pre><code class="hljs javascript"><span class="hljs-comment">// api/users/[id]/posts/[postId]/GET.js</span><br /><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">request, response</span>) </span>{<br /> <span class="hljs-keyword">const</span> { id, postId } = request.params;<br /> <br /> <span class="hljs-keyword">const</span> post = {<br /> id: postId,<br /> userId: id,<br /> title: <span class="hljs-string">`Post ${postId} by User ${id}`</span>,<br /> content: <span class="hljs-string">'This is the post content...'</span>,<br /> createdAt: <span class="hljs-string">'2024-01-01T00:00:00.000Z'</span><br /> };<br /> <br /> response.json(post);<br />}</code></pre>
|
|
49
|
+
|
|
50
|
+
<h3>HTML Route Example</h3>
|
|
51
|
+
<pre><code class="hljs html"><span class="hljs-comment"><!-- pages/about/GET.html --></span><br /><span class="hljs-meta"><!DOCTYPE html></span><br /><span class="hljs-tag"><<span class="hljs-name">html</span>></span><br /><span class="hljs-tag"><<span class="hljs-name">head</span>></span><br /> <span class="hljs-tag"><<span class="hljs-name">title</span>></span>About Us<span class="hljs-tag"></<span class="hljs-name">title</span>></span><br /><span class="hljs-tag"></<span class="hljs-name">head</span>></span><br /><span class="hljs-tag"><<span class="hljs-name">body</span>></span><br /> <span class="hljs-tag"><<span class="hljs-name">h1</span>></span>About Our Company<span class="hljs-tag"></<span class="hljs-name">h1</span>></span><br /> <span class="hljs-tag"><<span class="hljs-name">p</span>></span>We are a company that does amazing things.<span class="hljs-tag"></<span class="hljs-name">p</span>></span><br /><span class="hljs-tag"></<span class="hljs-name">body</span>></span><br /><span class="hljs-tag"></<span class="hljs-name">html</span>></span></code></pre>
|
|
52
|
+
|
|
53
|
+
<h2>Route File Structure Best Practices</h2>
|
|
54
|
+
|
|
55
|
+
<h3>Organize by Feature</h3>
|
|
56
|
+
<pre><code class="hljs markdown">api/<br />├─ auth/<br />│ ├─ login/<br />│ │ ├─ POST.js<br />│ ├─ logout/<br />│ │ ├─ POST.js<br />│ ├─ register/<br />│ │ ├─ POST.js<br />├─ users/<br />│ ├─ [id]/<br />│ │ ├─ GET.js<br />│ │ ├─ PUT.js<br />│ │ ├─ DELETE.js<br />│ ├─ GET.js<br />│ ├─ POST.js<br />├─ posts/<br />│ ├─ [id]/<br />│ │ ├─ GET.js<br />│ │ ├─ PUT.js<br />│ │ ├─ DELETE.js<br />│ ├─ GET.js<br />│ ├─ POST.js<br /></code></pre>
|
|
57
|
+
|
|
58
|
+
<h3>Use Index Files for Fallbacks</h3>
|
|
59
|
+
<p>Use <code>index.js</code> to handle methods not explicitly defined:</p>
|
|
60
|
+
<pre><code class="hljs javascript"><span class="hljs-comment">// api/users/index.js</span><br /><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">request, response</span>) </span>{<br /> <span class="hljs-comment">// Handle any method not explicitly defined</span><br /> response.status(<span class="hljs-number">405</span>).json({<br /> error: <span class="hljs-string">'Method not allowed'</span>,<br /> allowed: [<span class="hljs-string">'GET'</span>, <span class="hljs-string">'POST'</span>]<br /> });<br />}</code></pre>
|
|
61
|
+
|
|
62
|
+
<h2>Static File Serving</h2>
|
|
63
|
+
<p>Any file that doesn't match a route pattern will be served as a static file. This includes:</p>
|
|
64
|
+
<ul>
|
|
65
|
+
<li>HTML files (except route files)</li>
|
|
66
|
+
<li>CSS files</li>
|
|
67
|
+
<li>JavaScript files (except route files)</li>
|
|
68
|
+
<li>Images</li>
|
|
69
|
+
<li>Any other static assets</li>
|
|
70
|
+
</ul>
|
|
71
|
+
|
|
72
|
+
<p>Example static file structure:</p>
|
|
73
|
+
<pre><code class="hljs markdown">public/<br />├─ index.html # Served at /<br />├─ styles.css # Served at /styles.css<br />├─ script.js # Served at /script.js<br />├─ images/<br />│ ├─ logo.png # Served at /images/logo.png<br />├─ api/ # Routes directory<br />│ ├─ hello/GET.js # Route handler<br /></code></pre>
|
|
74
|
+
</main>
|
|
75
|
+
<div style="height:25vh"></div>
|
|
76
|
+
</body>
|
|
77
|
+
</html>
|
package/example-middleware.js
CHANGED
|
@@ -1,23 +1,23 @@
|
|
|
1
|
-
// Example custom middleware file
|
|
2
|
-
// This would be placed in your project directory and referenced in config
|
|
3
|
-
|
|
4
|
-
export default async function authMiddleware(req, res, next) {
|
|
5
|
-
// Example: Check for API key in headers
|
|
6
|
-
const apiKey = req.headers['x-api-key'];
|
|
7
|
-
|
|
8
|
-
// Skip auth for public routes
|
|
9
|
-
if (req.url.startsWith('/public/')) {
|
|
10
|
-
return await next();
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
if (!apiKey || apiKey !== process.env.API_KEY) {
|
|
14
|
-
res.writeHead(401, { 'Content-Type': 'application/json' });
|
|
15
|
-
res.end(JSON.stringify({ error: 'Unauthorized' }));
|
|
16
|
-
return;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
// Add user info to request for downstream use
|
|
20
|
-
req.user = { authenticated: true, apiKey };
|
|
21
|
-
|
|
22
|
-
await next();
|
|
23
|
-
}
|
|
1
|
+
// Example custom middleware file
|
|
2
|
+
// This would be placed in your project directory and referenced in config
|
|
3
|
+
|
|
4
|
+
export default async function authMiddleware(req, res, next) {
|
|
5
|
+
// Example: Check for API key in headers
|
|
6
|
+
const apiKey = req.headers['x-api-key'];
|
|
7
|
+
|
|
8
|
+
// Skip auth for public routes
|
|
9
|
+
if (req.url.startsWith('/public/')) {
|
|
10
|
+
return await next();
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
if (!apiKey || apiKey !== process.env.API_KEY) {
|
|
14
|
+
res.writeHead(401, { 'Content-Type': 'application/json' });
|
|
15
|
+
res.end(JSON.stringify({ error: 'Unauthorized' }));
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Add user info to request for downstream use
|
|
20
|
+
req.user = { authenticated: true, apiKey };
|
|
21
|
+
|
|
22
|
+
await next();
|
|
23
|
+
}
|