Exemples Bun.serve

Exemples de serveur web Bun.serve incluant serveur HTTP, WebSocket et service de fichiers statiques

💻 Bun.serve Hello World typescript

🟢 simple

Configuration de base du serveur HTTP Bun.serve et application Hello World

// Bun.serve Hello World Examples

// 1. Basic HTTP server
const server = Bun.serve({
    port: 3000,
    fetch(req) {
        return new Response("Hello, World!");
    },
});

console.log(`Listening on http://localhost:${server.port}...`);

// 2. Hello World with routing
const server = Bun.serve({
    port: 3000,
    fetch(req) {
        const url = new URL(req.url);

        switch (url.pathname) {
            case '/':
                return new Response('Hello, Bun!');
            case '/hello':
                return new Response('Hello from Bun.serve!');
            case '/hello/:name':
                const name = url.pathname.split('/')[2];
                return new Response(`Hello, ${name}!`);
            default:
                return new Response('Not Found', { status: 404 });
        }
    },
});

// 3. JSON response
const server = Bun.serve({
    port: 3000,
    fetch(req) {
        const url = new URL(req.url);

        if (url.pathname === '/api/hello') {
            return Response.json({
                message: 'Hello, World!',
                timestamp: new Date().toISOString(),
                version: '1.0.0',
                runtime: 'Bun'
            });
        }

        return new Response('Hello, World!');
    },
});

// 4. HTTP methods handling
const server = Bun.serve({
    port: 3000,
    fetch(req) {
        const url = new URL(req.url);
        const method = req.method;

        if (url.pathname === '/api/users') {
            switch (method) {
                case 'GET':
                    return Response.json(['Alice', 'Bob', 'Charlie']);
                case 'POST':
                    return new Response('User created', { status: 201 });
                default:
                    return new Response('Method Not Allowed', { status: 405 });
            }
        }

        return new Response('Hello, World!');
    },
});

// 5. Request body parsing
const server = Bun.serve({
    port: 3000,
    async fetch(req) {
        const url = new URL(req.url);

        if (url.pathname === '/api/hello' && req.method === 'POST') {
            const body = await req.json();
            return Response.json({
                greeting: `Hello, ${body.name || 'World'}!`,
                message: body.message || 'Welcome to Bun.serve',
                receivedAt: new Date().toISOString()
            });
        }

        return new Response('Hello, World!');
    },
});

// 6. HTML response
const server = Bun.serve({
    port: 3000,
    fetch(req) {
        const url = new URL(req.url);

        if (url.pathname === '/') {
            const html = `
                <!DOCTYPE html>
                <html>
                <head>
                    <title>Hello Bun</title>
                </head>
                <body>
                    <h1>Hello, World!</h1>
                    <p>This is an HTML response from Bun.serve</p>
                </body>
                </html>
            `;
            return new Response(html, {
                headers: { 'Content-Type': 'text/html' }
            });
        }

        return new Response('Hello, World!');
    },
});

// 7. Error handling
const server = Bun.serve({
    port: 3000,
    fetch(req) {
        const url = new URL(req.url);

        if (url.pathname === '/error') {
            throw new Error('Something went wrong!');
        }

        return new Response('Hello, World!');
    },
    error(error) {
        return new Response(`Error: ${error.message}`, { status: 500 });
    },
});

// 8. Server information
const server = Bun.serve({
    port: 3000,
    fetch(req) {
        const url = new URL(req.url);

        if (url.pathname === '/info') {
            return Response.json({
                server: {
                    hostname: server.hostname,
                    port: server.port,
                    development: server.development
                },
                request: {
                    method: req.method,
                    url: req.url,
                    headers: Object.fromEntries(req.headers.entries())
                }
            });
        }

        return new Response('Hello, World!');
    },
});

console.log(`🦊 Bun server running at http://localhost:${server.port}`);

💻 Serveur WebSocket Bun.serve typescript

🟡 intermediate

Implémentation de serveur WebSocket utilisant Bun.serve

// Bun.serve WebSocket Examples

// 1. Basic WebSocket server
const server = Bun.serve({
    port: 3000,
    fetch(req, server) {
        const url = new URL(req.url);

        if (url.pathname === '/ws') {
            const upgraded = server.upgrade(req);
            if (upgraded) {
                return undefined; // WebSocket handled
            }
        }

        return new Response('Not Found', { status: 404 });
    },
    websocket: {
        message(ws, message) {
            ws.send(`Echo: ${message}`);
        },
        open(ws) {
            console.log('WebSocket connection opened');
            ws.send('Welcome to Bun WebSocket server!');
        },
        close(ws) {
            console.log('WebSocket connection closed');
        },
    },
});

// 2. Chat server
interface ChatMessage {
    type: 'message' | 'join' | 'leave';
    user: string;
    content: string;
    timestamp: string;
}

const clients = new Set<WebSocket>();

const server = Bun.serve({
    port: 3000,
    fetch(req, server) {
        const url = new URL(req.url);

        if (url.pathname === '/chat') {
            const upgraded = server.upgrade(req);
            if (upgraded) {
                return undefined;
            }
        }

        return new Response('Chat server running');
    },
    websocket: {
        message(ws, message: string) {
            const chatMessage: ChatMessage = {
                type: 'message',
                user: ws.data.userId || 'Anonymous',
                content: message,
                timestamp: new Date().toISOString()
            };

            // Broadcast to all clients
            server.publish('chat', JSON.stringify(chatMessage));
        },
        open(ws) {
            const userId = `user_${Math.random().toString(36).substr(2, 9)}`;
            ws.data = { userId };

            clients.add(ws);
            ws.subscribe('chat');

            const joinMessage: ChatMessage = {
                type: 'join',
                user: 'System',
                content: `${userId} joined the chat`,
                timestamp: new Date().toISOString()
            };

            server.publish('chat', JSON.stringify(joinMessage));
        },
        close(ws) {
            clients.delete(ws);
            ws.unsubscribe('chat');

            const leaveMessage: ChatMessage = {
                type: 'leave',
                user: 'System',
                content: `${ws.data.userId} left the chat`,
                timestamp: new Date().toISOString()
            };

            server.publish('chat', JSON.stringify(leaveMessage));
        },
    },
});

// 3. Real-time notifications
const server = Bun.serve({
    port: 3000,
    fetch(req, server) {
        const url = new URL(req.url);

        if (url.pathname === '/notifications') {
            const upgraded = server.upgrade(req);
            if (upgraded) {
                return undefined;
            }
        }

        return new Response('Notification server running');
    },
    websocket: {
        open(ws) {
            console.log('Notification client connected');
            ws.data.interval = setInterval(() => {
                const notification = {
                    type: 'notification',
                    title: 'System Update',
                    message: `System time: ${new Date().toLocaleTimeString()}`,
                    timestamp: new Date().toISOString()
                };

                ws.send(JSON.stringify(notification));
            }, 5000);
        },
        close(ws) {
            if (ws.data.interval) {
                clearInterval(ws.data.interval);
            }
        },
    },
});

// 4. WebSocket with authentication
const server = Bun.serve({
    port: 3000,
    fetch(req, server) {
        const url = new URL(req.url);
        const auth = req.headers.get('authorization');

        if (url.pathname === '/auth-ws') {
            // Simple token validation
            if (!auth?.startsWith('Bearer ') || auth.slice(7) !== 'valid-token') {
                return new Response('Unauthorized', { status: 401 });
            }

            const upgraded = server.upgrade(req);
            if (upgraded) {
                return undefined;
            }
        }

        return new Response('WebSocket server with authentication');
    },
    websocket: {
        message(ws, message) {
            ws.send(`Authenticated message: ${message}`);
        },
        open(ws) {
            ws.send('WebSocket connection authenticated');
        },
    },
});

console.log(`🦊 Bun WebSocket server running at http://localhost:${server.port}`);

💻 Serveur de Fichiers Statiques Bun.serve typescript

🟡 intermediate

Service de fichiers statiques avec cache et liste de répertoires

// Bun.serve Static File Server Examples

// 1. Basic static file server
const server = Bun.serve({
    port: 3000,
    fetch(req) {
        const url = new URL(req.url);
        const filePath = url.pathname === '/' ? './public/index.html' : `./public${url.pathname}`;

        try {
            const file = Bun.file(filePath);
            return new Response(file);
        } catch (error) {
            return new Response('File not found', { status: 404 });
        }
    },
});

// 2. Static file server with caching
const server = Bun.serve({
    port: 3000,
    fetch(req) {
        const url = new URL(req.url);
        const filePath = url.pathname === '/' ? './public/index.html' : `./public${url.pathname}`;

        try {
            const file = Bun.file(filePath);

            return new Response(file, {
                headers: {
                    'Cache-Control': 'public, max-age=3600', // 1 hour cache
                    'Content-Type': getContentType(filePath)
                }
            });
        } catch (error) {
            return new Response('File not found', { status: 404 });
        }
    },
});

function getContentType(filePath: string): string {
    const ext = filePath.split('.').pop()?.toLowerCase();
    const types = {
        'html': 'text/html',
        'css': 'text/css',
        'js': 'application/javascript',
        'json': 'application/json',
        'png': 'image/png',
        'jpg': 'image/jpeg',
        'jpeg': 'image/jpeg',
        'gif': 'image/gif',
        'svg': 'image/svg+xml',
        'pdf': 'application/pdf',
        'txt': 'text/plain'
    };

    return types[ext] || 'application/octet-stream';
}

// 3. Static file server with directory listing
const server = Bun.serve({
    port: 3000,
    async fetch(req) {
        const url = new URL(req.url);
        const filePath = url.pathname === '/' ? './public' : `./public${url.pathname}`;

        try {
            const file = Bun.file(filePath);
            const exists = await file.exists();

            if (exists) {
                const isDirectory = filePath.endsWith('/') || !(await file.text()).startsWith('<');

                if (!isDirectory) {
                    return new Response(file, {
                        headers: { 'Content-Type': getContentType(filePath) }
                    });
                } else {
                    // Generate directory listing
                    const path = filePath.replace('./public', '');
                    const files = await scanDir(filePath);

                    const html = generateDirectoryListing(path, files);
                    return new Response(html, {
                        headers: { 'Content-Type': 'text/html' }
                    });
                }
            } else {
                return new Response('File not found', { status: 404 });
            }
        } catch (error) {
            return new Response('Server error', { status: 500 });
        }
    },
});

async function scanDir(dirPath: string): Promise<string[]> {
    try {
        const files = [];
        for await (const file of Bun.glob(`${dirPath}/*`)) {
            files.push(file.replace('./public/', ''));
        }
        return files;
    } catch (error) {
        return [];
    }
}

function generateDirectoryListing(path: string, files: string[]): string {
    return `
        <!DOCTYPE html>
        <html>
        <head>
            <title>Directory listing for ${path}</title>
            <style>
                body { font-family: Arial, sans-serif; margin: 20px; }
                .file-list { list-style: none; padding: 0; }
                .file-list li { margin: 5px 0; }
                .file-list a { text-decoration: none; color: #007bff; }
                .file-list a:hover { text-decoration: underline; }
            </style>
        </head>
        <body>
            <h1>Directory listing for ${path || '/'}</h1>
            <ul class="file-list">
                ${path ? '<li><a href="../">../</a></li>' : ''}
                ${files.map(file => {
                    const isDirectory = file.endsWith('/');
                    return `<li><a href="${file}">${file}</a></li>`;
                }).join('')}
            </ul>
        </body>
        </html>
    `;
}

// 4. Static file server with compression
const server = Bun.serve({
    port: 3000,
    async fetch(req) {
        const url = new URL(req.url);
        const filePath = url.pathname === '/' ? './public/index.html' : `./public${url.pathname}`;

        try {
            const file = Bun.file(filePath);
            const exists = await file.exists();

            if (!exists) {
                return new Response('File not found', { status: 404 });
            }

            const acceptEncoding = req.headers.get('accept-encoding') || '';
            const shouldGzip = acceptEncoding.includes('gzip') &&
                             filePath.match(/\.(js|css|html|txt|json|svg)$/);

            if (shouldGzip) {
                const compressed = await Bun.gzip(await file.arrayBuffer());
                return new Response(compressed, {
                    headers: {
                        'Content-Type': getContentType(filePath),
                        'Content-Encoding': 'gzip',
                        'Cache-Control': 'public, max-age=3600'
                    }
                });
            }

            return new Response(file, {
                headers: {
                    'Content-Type': getContentType(filePath),
                    'Cache-Control': 'public, max-age=3600'
                }
            });
        } catch (error) {
            return new Response('Server error', { status: 500 });
        }
    },
});

console.log(`🦊 Bun static file server running at http://localhost:${server.port}`);

💻 Serveur REST API Bun.serve typescript

🟡 intermediate

Serveur REST API complet avec opérations CRUD et middleware

// Bun.serve REST API Server Examples

interface User {
    id: number;
    name: string;
    email: string;
    createdAt: string;
}

interface Todo {
    id: number;
    title: string;
    completed: boolean;
    userId: number;
    createdAt: string;
}

// In-memory database
let users: User[] = [
    { id: 1, name: 'John Doe', email: '[email protected]', createdAt: new Date().toISOString() },
    { id: 2, name: 'Jane Smith', email: '[email protected]', createdAt: new Date().toISOString() }
];

let todos: Todo[] = [
    { id: 1, title: 'Learn Bun.serve', completed: false, userId: 1, createdAt: new Date().toISOString() },
    { id: 2, title: 'Build REST API', completed: true, userId: 1, createdAt: new Date().toISOString() }
];

let userIdCounter = 3;
let todoIdCounter = 3;

// 1. Basic REST API server
const server = Bun.serve({
    port: 3000,
    async fetch(req) {
        const url = new URL(req.url);
        const method = req.method;
        const path = url.pathname;

        // CORS headers
        const corsHeaders = {
            'Access-Control-Allow-Origin': '*',
            'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
            'Access-Control-Allow-Headers': 'Content-Type, Authorization'
        };

        // Handle OPTIONS requests for CORS
        if (method === 'OPTIONS') {
            return new Response(null, { headers: corsHeaders });
        }

        try {
            // Users API
            if (path.startsWith('/api/users')) {
                if (path === '/api/users') {
                    if (method === 'GET') {
                        return Response.json(users, { headers: corsHeaders });
                    } else if (method === 'POST') {
                        const body = await req.json();
                        const newUser: User = {
                            id: userIdCounter++,
                            name: body.name,
                            email: body.email,
                            createdAt: new Date().toISOString()
                        };
                        users.push(newUser);
                        return Response.json(newUser, {
                            status: 201,
                            headers: corsHeaders
                        });
                    }
                } else if (path.match(/^\/api\/users\/\d+$/)) {
                    const userId = parseInt(path.split('/').pop()!);
                    const userIndex = users.findIndex(u => u.id === userId);

                    if (userIndex === -1) {
                        return Response.json({ error: 'User not found' }, {
                            status: 404,
                            headers: corsHeaders
                        });
                    }

                    if (method === 'GET') {
                        return Response.json(users[userIndex], { headers: corsHeaders });
                    } else if (method === 'PUT') {
                        const body = await req.json();
                        users[userIndex] = { ...users[userIndex], ...body };
                        return Response.json(users[userIndex], { headers: corsHeaders });
                    } else if (method === 'DELETE') {
                        const deletedUser = users.splice(userIndex, 1)[0];
                        return Response.json(deletedUser, { headers: corsHeaders });
                    }
                }
            }

            // Todos API
            if (path.startsWith('/api/todos')) {
                if (path === '/api/todos') {
                    if (method === 'GET') {
                        const userId = url.searchParams.get('userId');
                        const userTodos = userId ?
                            todos.filter(t => t.userId === parseInt(userId)) :
                            todos;
                        return Response.json(userTodos, { headers: corsHeaders });
                    } else if (method === 'POST') {
                        const body = await req.json();
                        const newTodo: Todo = {
                            id: todoIdCounter++,
                            title: body.title,
                            completed: false,
                            userId: body.userId,
                            createdAt: new Date().toISOString()
                        };
                        todos.push(newTodo);
                        return Response.json(newTodo, {
                            status: 201,
                            headers: corsHeaders
                        });
                    }
                } else if (path.match(/^\/api\/todos\/\d+$/)) {
                    const todoId = parseInt(path.split('/').pop()!);
                    const todoIndex = todos.findIndex(t => t.id === todoId);

                    if (todoIndex === -1) {
                        return Response.json({ error: 'Todo not found' }, {
                            status: 404,
                            headers: corsHeaders
                        });
                    }

                    if (method === 'GET') {
                        return Response.json(todos[todoIndex], { headers: corsHeaders });
                    } else if (method === 'PUT') {
                        const body = await req.json();
                        todos[todoIndex] = { ...todos[todoIndex], ...body };
                        return Response.json(todos[todoIndex], { headers: corsHeaders });
                    } else if (method === 'DELETE') {
                        const deletedTodo = todos.splice(todoIndex, 1)[0];
                        return Response.json(deletedTodo, { headers: corsHeaders });
                    }
                }
            }

            // Health check
            if (path === '/health') {
                return Response.json({
                    status: 'ok',
                    timestamp: new Date().toISOString(),
                    uptime: process.uptime(),
                    stats: {
                        users: users.length,
                        todos: todos.length
                    }
                }, { headers: corsHeaders });
            }

            return new Response('Not Found', { status: 404, headers: corsHeaders });
        } catch (error) {
            return Response.json({
                error: 'Internal Server Error',
                message: error.message
            }, { status: 500, headers: corsHeaders });
        }
    },
});

// 2. API server with middleware
const createApiServer = () => {
    const middlewares: Array<(req: Request, next: () => Promise<Response>) => Promise<Response>> = [];

    // CORS middleware
    const corsMiddleware = async (req: Request, next: () => Promise<Response>) => {
        const response = await next();

        // Add CORS headers to all responses
        Object.entries({
            'Access-Control-Allow-Origin': '*',
            'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
            'Access-Control-Allow-Headers': 'Content-Type, Authorization'
        }).forEach(([key, value]) => {
            response.headers.set(key, value);
        });

        return response;
    };

    // Logging middleware
    const loggingMiddleware = async (req: Request, next: () => Promise<Response>) => {
        const start = Date.now();
        console.log(`[${new Date().toISOString()}] ${req.method} ${req.url}`);

        const response = await next();

        const duration = Date.now() - start;
        console.log(`[${new Date().toISOString()}] ${req.method} ${req.url} - ${response.status} (${duration}ms)`);

        return response;
    };

    // Rate limiting middleware
    const rateLimitMap = new Map<string, { count: number; resetTime: number }>();
    const rateLimitMiddleware = async (req: Request, next: () => Promise<Response>) => {
        const ip = req.headers.get('x-forwarded-for') || 'unknown';
        const now = Date.now();
        const windowMs = 60000; // 1 minute
        const maxRequests = 100;

        const record = rateLimitMap.get(ip);

        if (!record || now > record.resetTime) {
            rateLimitMap.set(ip, { count: 1, resetTime: now + windowMs });
            return await next();
        }

        if (record.count >= maxRequests) {
            return new Response('Too Many Requests', { status: 429 });
        }

        record.count++;
        return await next();
    };

    middlewares.push(corsMiddleware, loggingMiddleware, rateLimitMiddleware);

    // Request handler
    const handler = async (req: Request): Promise<Response> => {
        const url = new URL(req.url);
        const path = url.pathname;

        // API routes
        if (path === '/api/data') {
            return Response.json({
                message: 'Hello from middleware!',
                timestamp: new Date().toISOString()
            });
        }

        return new Response('Not Found', { status: 404 });
    };

    // Execute middleware chain
    const executeMiddleware = async (req: Request, index = 0): Promise<Response> => {
        if (index >= middlewares.length) {
            return await handler(req);
        }

        return await middlewares[index](req, () => executeMiddleware(req, index + 1));
    };

    return executeMiddleware;
};

const serverWithMiddleware = Bun.serve({
    port: 3001,
    fetch: createApiServer(),
});

console.log(`🦊 Bun API server running at http://localhost:${server.port}`);
console.log(`🦊 Bun API server with middleware running at http://localhost:${serverWithMiddleware.port}`);