0
# HTTP Server and Middleware
1
2
The Server class provides a built-in HTTP server for receiving GitHub webhooks, serving web interfaces, and handling custom HTTP requests with extensible middleware support.
3
4
## Capabilities
5
6
### Server Class
7
8
Built-in HTTP server with webhook handling, static file serving, and custom route support.
9
10
```typescript { .api }
11
/**
12
* HTTP server for handling webhooks and web requests
13
*/
14
class Server {
15
/**
16
* Create a new server instance
17
* @param options - Server configuration options
18
*/
19
constructor(options: ServerOptions = {});
20
21
/**
22
* Get the Probot framework version
23
* @returns Version string
24
*/
25
static get version(): string;
26
}
27
```
28
29
**Usage Examples:**
30
31
```typescript
32
import { Server, Probot } from "probot";
33
34
// Basic server setup
35
const server = new Server({
36
port: 3000,
37
host: "localhost",
38
Probot: Probot,
39
});
40
41
// Server with custom options
42
const server = new Server({
43
port: process.env.PORT || 3000,
44
webhookPath: "/api/github/webhooks",
45
enablePing: true,
46
enableStaticFiles: true,
47
loggingOptions: {
48
level: "info",
49
},
50
});
51
```
52
53
### Server Properties
54
55
Access to server configuration and runtime properties.
56
57
```typescript { .api }
58
/**
59
* Port the server is configured to listen on
60
*/
61
get port(): number;
62
63
/**
64
* Host the server is configured to bind to
65
*/
66
get host(): string;
67
68
/**
69
* Server instance version (same as Probot version)
70
*/
71
get version(): string;
72
```
73
74
**Usage Examples:**
75
76
```typescript
77
const server = new Server({ port: 3000, host: "0.0.0.0" });
78
79
console.log(`Server will start on ${server.host}:${server.port}`);
80
console.log(`Server version: ${server.version}`);
81
```
82
83
### Handler Management
84
85
Methods for registering custom HTTP request handlers.
86
87
```typescript { .api }
88
/**
89
* Add a custom HTTP request handler
90
* @param handler - Handler function for processing HTTP requests
91
*/
92
addHandler(handler: Handler): void;
93
94
/**
95
* Load a handler factory function
96
* @param appFn - Factory function that returns a handler
97
*/
98
async loadHandlerFactory(appFn: HandlerFactory): Promise<void>;
99
100
/**
101
* Load an application function that may register handlers
102
* @param appFn - Application function to load
103
*/
104
async load(appFn: ApplicationFunction): Promise<void>;
105
```
106
107
**Usage Examples:**
108
109
```typescript
110
// Add custom handler
111
server.addHandler((req, res) => {
112
if (req.url === "/health") {
113
res.writeHead(200, { "Content-Type": "application/json" });
114
res.end(JSON.stringify({ status: "ok", timestamp: new Date().toISOString() }));
115
return true; // Handler processed the request
116
}
117
return false; // Pass to next handler
118
});
119
120
// Handler factory
121
const createAPIHandler = (app: Probot, options) => {
122
return (req, res) => {
123
if (req.url?.startsWith("/api/v1/")) {
124
// Custom API logic
125
res.writeHead(200, { "Content-Type": "application/json" });
126
res.end(JSON.stringify({ message: "API endpoint" }));
127
return true;
128
}
129
return false;
130
};
131
};
132
133
await server.loadHandlerFactory(createAPIHandler);
134
135
// Application function that adds handlers
136
const appWithRoutes = (app: Probot, { addHandler }) => {
137
// Register webhook handlers
138
app.on("issues.opened", async (context) => {
139
// Handle webhook
140
});
141
142
// Add custom HTTP routes
143
addHandler((req, res) => {
144
if (req.url === "/dashboard") {
145
res.writeHead(200, { "Content-Type": "text/html" });
146
res.end("<h1>Dashboard</h1>");
147
return true;
148
}
149
return false;
150
});
151
};
152
153
await server.load(appWithRoutes);
154
```
155
156
### Server Lifecycle
157
158
Methods for starting and stopping the HTTP server.
159
160
```typescript { .api }
161
/**
162
* Start the HTTP server with all configured handlers
163
* Sets up default handlers for webhooks, ping, static files, and 404s
164
* @returns Promise resolving to Node.js HTTP server instance
165
*/
166
async start(): Promise<HttpServer>;
167
168
/**
169
* Stop the HTTP server and clean up resources
170
* Closes webhook proxy connections if configured
171
*/
172
async stop(): Promise<void>;
173
```
174
175
**Usage Examples:**
176
177
```typescript
178
import { Server, Probot } from "probot";
179
180
const server = new Server({
181
port: 3000,
182
Probot: Probot,
183
});
184
185
// Load application
186
await server.load((app) => {
187
app.on("push", async (context) => {
188
context.log.info("Push received");
189
});
190
});
191
192
// Start server
193
const httpServer = await server.start();
194
console.log(`Server started on port ${server.port}`);
195
196
// Graceful shutdown
197
process.on("SIGTERM", async () => {
198
console.log("Shutting down server...");
199
await server.stop();
200
process.exit(0);
201
});
202
```
203
204
## Built-in Handlers
205
206
### Webhook Handler
207
208
Automatically configured to handle GitHub webhook events at the specified webhook path.
209
210
```typescript { .api }
211
// Default webhook path: "/api/github/webhooks"
212
// Handles POST requests with GitHub webhook payloads
213
// Verifies webhook signatures using the configured secret
214
// Parses payloads and triggers registered event handlers
215
```
216
217
### Ping Handler
218
219
Optional health check endpoint for monitoring server availability.
220
221
```typescript { .api }
222
// Endpoint: GET /ping
223
// Response: "PONG" with 200 status
224
// Enabled with: enablePing: true in ServerOptions
225
```
226
227
**Usage Examples:**
228
229
```typescript
230
const server = new Server({
231
enablePing: true,
232
});
233
234
// GET /ping -> "PONG"
235
```
236
237
### Static Files Handler
238
239
Optional static file serving for web interfaces and assets.
240
241
```typescript { .api }
242
// Serves files from ./static directory
243
// Endpoint: GET /static/*
244
// Enabled with: enableStaticFiles: true in ServerOptions
245
```
246
247
**Usage Examples:**
248
249
```typescript
250
const server = new Server({
251
enableStaticFiles: true,
252
});
253
254
// Files in ./static/logo.png -> GET /static/logo.png
255
// Files in ./static/css/styles.css -> GET /static/css/styles.css
256
```
257
258
### Not Found Handler
259
260
Default 404 handler for unmatched requests.
261
262
```typescript { .api }
263
// Returns 404 status for unhandled requests
264
// Enabled with: enableNotFound: true in ServerOptions (default: true)
265
// Can be disabled to allow custom 404 handling
266
```
267
268
## Configuration Types
269
270
```typescript { .api }
271
interface ServerOptions {
272
/** Current working directory */
273
cwd?: string;
274
/** Logger instance for server logs */
275
log?: Logger;
276
/** Port to listen on */
277
port?: number;
278
/** Host to bind to */
279
host?: string;
280
/** Webhook endpoint path */
281
webhookPath?: string;
282
/** Smee.io proxy URL for development */
283
webhookProxy?: string;
284
/** Enable GET /ping endpoint */
285
enablePing?: boolean;
286
/** Enable 404 not found handler */
287
enableNotFound?: boolean;
288
/** Enable static file serving from ./static */
289
enableStaticFiles?: boolean;
290
/** Probot class to use for app instances */
291
Probot: typeof Probot;
292
/** HTTP request logging configuration */
293
loggingOptions?: LoggingOptions;
294
/** Octokit request configuration */
295
request?: RequestRequestOptions;
296
}
297
298
type Handler = (
299
req: IncomingMessage,
300
res: ServerResponse
301
) => void | boolean | Promise<void | boolean>;
302
303
type HandlerFactory = (
304
app: Probot,
305
options: ApplicationFunctionOptions
306
) => Handler | Promise<Handler>;
307
308
type ApplicationFunction = (
309
app: Probot,
310
options: ApplicationFunctionOptions
311
) => void | Promise<void>;
312
313
interface LoggingOptions {
314
/** Log level for HTTP requests */
315
level?: "trace" | "debug" | "info" | "warn" | "error" | "fatal" | "silent";
316
/** Custom logger instance */
317
logger?: Logger;
318
/** Custom serializers for request/response logging */
319
serializers?: Record<string, (obj: any) => any>;
320
}
321
322
interface ApplicationFunctionOptions {
323
/** Current working directory */
324
cwd: string;
325
/** Function to register custom HTTP handlers */
326
addHandler: (handler: Handler) => void;
327
/** Additional options */
328
[key: string]: unknown;
329
}
330
```
331
332
## Middleware Integration
333
334
### Express Integration
335
336
```typescript
337
import express from "express";
338
import { createNodeMiddleware } from "probot";
339
340
const app = express();
341
342
// Create Probot middleware
343
const probotMiddleware = await createNodeMiddleware((app) => {
344
app.on("issues.opened", async (context) => {
345
// Handle GitHub webhooks
346
});
347
});
348
349
// Mount Probot middleware
350
app.use("/github", probotMiddleware);
351
352
// Add other Express routes
353
app.get("/", (req, res) => {
354
res.send("Hello World");
355
});
356
357
app.listen(3000);
358
```
359
360
### Fastify Integration
361
362
```typescript
363
import Fastify from "fastify";
364
import { createNodeMiddleware } from "probot";
365
366
const fastify = Fastify();
367
368
// Create Probot handler
369
const probotHandler = await createNodeMiddleware((app) => {
370
app.on("push", async (context) => {
371
// Handle webhooks
372
});
373
});
374
375
// Register as Fastify plugin
376
fastify.register(async (fastify) => {
377
fastify.all("/webhooks/*", async (request, reply) => {
378
return probotHandler(request.raw, reply.raw);
379
});
380
});
381
382
await fastify.listen({ port: 3000 });
383
```
384
385
### Custom HTTP Server
386
387
```typescript
388
import { createServer } from "http";
389
import { createNodeMiddleware } from "probot";
390
391
const middleware = await createNodeMiddleware((app) => {
392
app.on("repository.created", async (context) => {
393
context.log.info("New repository created");
394
});
395
});
396
397
const server = createServer(async (req, res) => {
398
// Try Probot middleware first
399
const handled = await middleware(req, res);
400
401
if (!handled) {
402
// Handle other routes
403
res.writeHead(404);
404
res.end("Not Found");
405
}
406
});
407
408
server.listen(3000);
409
```
410
411
## Types
412
413
```typescript { .api }
414
type HandlerFactory = (
415
app: Probot,
416
options: ApplicationFunctionOptions
417
) => Handler | Promise<Handler>;
418
```