Skip to content

Commit 2c43a20

Browse files
committed
feat(router): enhance serve method with hostname and graceful shutdown 🛠️
- Add hostname parameter support for server binding - Add AbortSignal parameter for graceful shutdown - Add method overloads for flexible serve configuration - Add server configuration documentation - Update navigation config to include server configuration guide
1 parent d00d938 commit 2c43a20

3 files changed

Lines changed: 274 additions & 3 deletions

File tree

docs/.vitepress/config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ export default defineConfig({
1515
items: [
1616
{ text: 'Installation', link: '/getting-started/installation' },
1717
{ text: 'Quick Start', link: '/getting-started/quick-start' },
18+
{ text: 'Server Configuration', link: '/getting-started/server-configuration' },
1819
{ text: 'Custom Configuration', link: '/getting-started/custom-configuration' }
1920
]
2021
},
Lines changed: 250 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,250 @@
1+
# Server Configuration
2+
3+
> **Reference**: [Deno.serve API Documentation](https://docs.deno.com/api/deno/~/Deno.serve)
4+
5+
Configure your Deserve server with hostname binding and graceful shutdown.
6+
7+
## Basic Server Setup
8+
9+
The simplest way to start a server:
10+
11+
```typescript
12+
import { Router } from '@neabyte/deserve'
13+
14+
const router = new Router()
15+
16+
// Start server on default port 8000
17+
await router.serve(8000)
18+
```
19+
20+
This starts your server on `0.0.0.0:8000` (all interfaces).
21+
22+
## Enhanced Serve Method
23+
24+
Deserve's enhanced `serve` method supports three parameters:
25+
26+
```typescript
27+
// Method signatures
28+
async serve(port: number): Promise<void>
29+
async serve(port: number, hostname?: string): Promise<void>
30+
async serve(port: number, hostname?: string, signal?: AbortSignal): Promise<void>
31+
```
32+
33+
## Hostname Binding
34+
35+
### Bind to Specific Interface
36+
37+
```typescript
38+
// Bind to localhost only
39+
await router.serve(8000, '127.0.0.1')
40+
41+
// Bind to all interfaces (default)
42+
await router.serve(8000, '0.0.0.0')
43+
44+
// Bind to specific network interface
45+
await router.serve(8000, '192.168.1.100')
46+
```
47+
48+
### Development vs Production
49+
50+
```typescript
51+
// Development - localhost only
52+
await router.serve(8000, '127.0.0.1')
53+
54+
// Production - all interfaces
55+
await router.serve(8000, '0.0.0.0')
56+
```
57+
58+
## Graceful Shutdown
59+
60+
Use `AbortSignal` for graceful server shutdown:
61+
62+
```typescript
63+
import { Router } from '@neabyte/deserve'
64+
65+
const router = new Router()
66+
const ac = new AbortController()
67+
68+
// Start server with abort signal
69+
await router.serve(8000, '127.0.0.1', ac.signal)
70+
71+
// Graceful shutdown
72+
console.log('Shutting down server...')
73+
ac.abort()
74+
```
75+
76+
### Process Signal Handling
77+
78+
```typescript
79+
import { Router } from '@neabyte/deserve'
80+
81+
const router = new Router()
82+
const ac = new AbortController()
83+
84+
// Handle SIGINT (Ctrl+C)
85+
process.on('SIGINT', async () => {
86+
console.log('Received SIGINT, shutting down gracefully...')
87+
ac.abort()
88+
})
89+
90+
// Handle SIGTERM
91+
process.on('SIGTERM', async () => {
92+
console.log('Received SIGTERM, shutting down gracefully...')
93+
ac.abort()
94+
})
95+
96+
await router.serve(8000, '127.0.0.1', ac.signal)
97+
```
98+
99+
## Environment-Based Configuration
100+
101+
### Using Environment Variables
102+
103+
```typescript
104+
import { Router } from '@neabyte/deserve'
105+
106+
const router = new Router()
107+
108+
const port = parseInt(Deno.env.get('PORT') ?? '8000')
109+
const hostname = Deno.env.get('HOSTNAME') ?? '0.0.0.0'
110+
111+
await router.serve(port, hostname)
112+
```
113+
114+
### Development Configuration
115+
116+
```typescript
117+
// main.ts
118+
import { Router } from '@neabyte/deserve'
119+
120+
const router = new Router()
121+
122+
const isDev = Deno.env.get('NODE_ENV') === 'development'
123+
const port = isDev ? 3000 : 8000
124+
const hostname = isDev ? '127.0.0.1' : '0.0.0.0'
125+
126+
await router.serve(port, hostname)
127+
```
128+
129+
## Port Management
130+
131+
### Dynamic Port Assignment
132+
133+
```typescript
134+
// Let the system assign an available port
135+
await router.serve(0, '127.0.0.1')
136+
```
137+
138+
### Port Validation
139+
140+
```typescript
141+
const port = parseInt(Deno.env.get('PORT') ?? '8000')
142+
143+
// Validate port range
144+
if (port < 1 || port > 65535) {
145+
throw new Error('Port must be between 1 and 65535')
146+
}
147+
148+
await router.serve(port)
149+
```
150+
151+
## Error Handling
152+
153+
### Invalid Hostname
154+
155+
```typescript
156+
// ❌ Invalid hostname
157+
await router.serve(8000, 'invalid-hostname')
158+
// Will throw network error
159+
160+
// ✅ Valid hostname
161+
await router.serve(8000, '127.0.0.1')
162+
```
163+
164+
### Port Already in Use
165+
166+
```typescript
167+
try {
168+
await router.serve(8000)
169+
} catch (error) {
170+
if (error.message.includes('EADDRINUSE')) {
171+
console.log('Port 8000 is already in use')
172+
// Try alternative port
173+
await router.serve(8001)
174+
}
175+
}
176+
```
177+
178+
## Complete Example
179+
180+
Here's a production-ready server configuration:
181+
182+
```typescript
183+
// main.ts
184+
import { Router } from '@neabyte/deserve'
185+
186+
const router = new Router()
187+
const ac = new AbortController()
188+
189+
// Configuration from environment
190+
const port = parseInt(Deno.env.get('PORT') ?? '8000')
191+
const hostname = Deno.env.get('HOSTNAME') ?? '0.0.0.0'
192+
193+
// Graceful shutdown handlers
194+
process.on('SIGINT', () => {
195+
console.log('Received SIGINT, shutting down...')
196+
ac.abort()
197+
})
198+
199+
process.on('SIGTERM', () => {
200+
console.log('Received SIGTERM, shutting down...')
201+
ac.abort()
202+
})
203+
204+
// Start server
205+
console.log(`Starting server on ${hostname}:${port}`)
206+
await router.serve(port, hostname, ac.signal)
207+
```
208+
209+
## Testing Your Configuration
210+
211+
### Test Basic Server
212+
213+
```bash
214+
# Start server
215+
deno run --allow-net main.ts
216+
217+
# Test endpoint
218+
curl http://localhost:8000
219+
```
220+
221+
### Test Hostname Binding
222+
223+
```bash
224+
# Bind to localhost only
225+
deno run --allow-net main.ts
226+
227+
# Should work
228+
curl http://127.0.0.1:8000
229+
230+
# Should fail (if binding to 127.0.0.1 only)
231+
curl http://0.0.0.0:8000
232+
```
233+
234+
### Test Graceful Shutdown
235+
236+
```bash
237+
# Start server
238+
deno run --allow-net main.ts
239+
240+
# Send SIGINT (Ctrl+C)
241+
# Server should shutdown gracefully
242+
```
243+
244+
## Next Steps
245+
246+
Now that you know how to configure your server:
247+
248+
- [Custom Configuration](/getting-started/custom-configuration) - Configure router options
249+
- [Middleware](/middleware/global) - Add request processing
250+
- [Error Handling](/error-handling/object-details) - Handle errors gracefully

src/Router.ts

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,11 +84,31 @@ export class Router {
8484

8585
/**
8686
* Start server with file-based routing.
87-
* @param port - Port number to serve on
87+
* @param port - Port number to serve on (default: 8000)
88+
* @param hostname - Hostname to bind to (default: "0.0.0.0")
89+
* @param signal - Abort signal for server control
8890
*/
89-
async serve(port = 8000): Promise<void> {
91+
async serve(port?: number): Promise<void>
92+
async serve(port?: number, hostname?: string): Promise<void>
93+
async serve(port?: number, hostname?: string, signal?: AbortSignal): Promise<void>
94+
async serve(port?: number, hostname?: string, signal?: AbortSignal): Promise<void> {
9095
await this.initializeRoutes()
91-
Deno.serve({ port }, this.createHandler())
96+
const actualPort = port ?? 8000
97+
const actualHostname = hostname ?? '0.0.0.0'
98+
if (signal) {
99+
Deno.serve({
100+
port: actualPort,
101+
hostname: actualHostname,
102+
signal,
103+
handler: this.createHandler()
104+
})
105+
} else {
106+
Deno.serve({
107+
port: actualPort,
108+
hostname: actualHostname,
109+
handler: this.createHandler()
110+
})
111+
}
92112
}
93113

94114
/**

0 commit comments

Comments
 (0)