🎯 Exemples recommandés
Balanced sample collections from various categories for you to explore
Exemples Deno Deploy
Exemples de la plateforme de calcul de bord Deno Deploy incluant les fonctions serverless, les APIs et les applications en temps réel avec déploiement sans configuration
💻 Hello World de Base typescript
🟢 simple
Introduction simple aux applications Deno Deploy
// Deno Deploy Hello World
// main.ts - Entry point for Deno Deploy application
import { serve } from "https://deno.land/[email protected]/http/server.ts";
import { Router } from "https://deno.land/x/[email protected]/mod.ts";
// 1. Basic HTTP Server
async function basicHandler(request: Request): Promise<Response> {
const url = new URL(request.url);
const pathname = url.pathname;
// Route handling
switch (pathname) {
case "/":
return new Response(
JSON.stringify({
message: "Hello from Deno Deploy! 🦕",
timestamp: new Date().toISOString(),
method: request.method,
url: request.url,
}),
{
status: 200,
headers: {
"Content-Type": "application/json",
"Cache-Control": "public, max-age=3600",
},
}
);
case "/health":
return new Response(
JSON.stringify({
status: "healthy",
timestamp: new Date().toISOString(),
uptime: performance.now(),
}),
{
status: 200,
headers: { "Content-Type": "application/json" },
}
);
case "/echo":
if (request.method === "POST") {
const body = await request.text();
return new Response(
JSON.stringify({
echo: body,
method: request.method,
headers: Object.fromEntries(request.headers.entries()),
}),
{
status: 200,
headers: { "Content-Type": "application/json" },
}
);
} else {
return new Response("Method not allowed", { status: 405 });
}
default:
return new Response("Not Found", { status: 404 });
}
}
// 2. Oak Router Example
const router = new Router();
router.get("/", (ctx) => {
ctx.response.body = {
message: "Hello from Deno Deploy with Oak Router! 🦕",
framework: "Oak",
timestamp: new Date().toISOString(),
};
});
router.get("/api/users", (ctx) => {
ctx.response.body = {
users: [
{ id: 1, name: "Alice", email: "[email protected]" },
{ id: 2, name: "Bob", email: "[email protected]" },
],
total: 2,
};
});
router.get("/api/users/:id", (ctx) => {
const id = ctx.params.id;
ctx.response.body = {
user: { id, name: `User ${id}`, email: `user${id}@example.com` },
};
});
router.post("/api/users", async (ctx) => {
const body = ctx.request.body({ type: "json" });
const user = await body.value;
ctx.response.body = {
message: "User created successfully",
user: { id: Date.now(), ...user },
};
ctx.response.status = 201;
});
// 3. Middleware Example
async function corsMiddleware(request: Request, next: () => Promise<Response>): Promise<Response> {
const response = await next();
// Add CORS headers
response.headers.set("Access-Control-Allow-Origin", "*");
response.headers.set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
response.headers.set("Access-Control-Allow-Headers", "Content-Type, Authorization");
return response;
}
async function loggingMiddleware(request: Request, next: () => Promise<Response>): Promise<Response> {
const start = Date.now();
const response = await next();
const duration = Date.now() - start;
console.log(`${request.method} ${request.url} - ${response.status} - ${duration}ms`);
response.headers.set("X-Response-Time", `${duration}ms`);
return response;
}
// 4. Advanced Handler with Middleware Chain
async function advancedHandler(request: Request): Promise<Response> {
// Create middleware chain
const middleware = [
corsMiddleware,
loggingMiddleware,
];
let response: Response;
for (const middleware of middleware) {
response = await middleware(request, () => {
return router.routes()(request);
});
}
return response!;
}
// 5. Environment Variables and Configuration
interface Env {
API_KEY?: string;
DATABASE_URL?: string;
DEBUG?: string;
}
function getConfig(): Env {
return {
API_KEY: Deno.env.get("API_KEY"),
DATABASE_URL: Deno.env.get("DATABASE_URL"),
DEBUG: Deno.env.get("DEBUG"),
};
}
// 6. Error Handling
class AppError extends Error {
constructor(public statusCode: number, message: string) {
super(message);
this.name = "AppError";
}
}
async function errorHandler(request: Request, next: () => Promise<Response>): Promise<Response> {
try {
return await next();
} catch (error) {
console.error("Error handling request:", error);
if (error instanceof AppError) {
return new Response(
JSON.stringify({ error: error.message }),
{
status: error.statusCode,
headers: { "Content-Type": "application/json" },
}
);
}
return new Response(
JSON.stringify({ error: "Internal Server Error" }),
{
status: 500,
headers: { "Content-Type": "application/json" },
}
);
}
}
// 7. Main Server Function
async function handler(request: Request): Promise<Response> {
const config = getConfig();
// Add config to request headers for debugging
if (config.DEBUG === "true") {
console.log("Request received:", request.method, request.url);
console.log("Environment:", config);
}
return errorHandler(request, () => advancedHandler(request));
}
// 8. Start server
if (import.meta.main) {
console.log("Starting Deno Deploy server...");
await handler(new Request("https://example.com/"));
}
// Export for Deno Deploy
export default {
async fetch(request: Request): Promise<Response> {
return handler(request);
},
};
💻 Communication Temps Réel WebSocket typescript
🟡 intermediate
Implémenter la fonctionnalité de communication WebSocket en temps réel
// Deno Deploy WebSocket Chat Application
// websocket.ts - Real-time chat with WebSocket support
import { serve } from "https://deno.land/[email protected]/http/server.ts";
// 1. WebSocket Connection Manager
interface Connection {
id: string;
ws: WebSocket;
room: string;
username: string;
joinedAt: Date;
}
class ConnectionManager {
private connections = new Map<string, Connection>();
private rooms = new Map<string, Set<string>>();
addConnection(connection: Connection): void {
this.connections.set(connection.id, connection);
// Add to room
if (!this.rooms.has(connection.room)) {
this.rooms.set(connection.room, new Set());
}
this.rooms.get(connection.room)!.add(connection.id);
console.log(`Connection ${connection.id} joined room ${connection.room}`);
}
removeConnection(connectionId: string): void {
const connection = this.connections.get(connectionId);
if (connection) {
// Remove from room
const room = this.rooms.get(connection.room);
if (room) {
room.delete(connectionId);
if (room.size === 0) {
this.rooms.delete(connection.room);
}
}
this.connections.delete(connectionId);
console.log(`Connection ${connectionId} removed`);
}
}
getRoomConnections(room: string): Connection[] {
const roomConnections = this.rooms.get(room);
if (!roomConnections) return [];
return Array.from(roomConnections)
.map(id => this.connections.get(id))
.filter(Boolean) as Connection[];
}
broadcastToRoom(room: string, message: any, excludeId?: string): void {
const connections = this.getRoomConnections(room);
connections.forEach(connection => {
if (connection.id !== excludeId) {
connection.ws.send(JSON.stringify(message));
}
});
}
getRoomStats(): Record<string, number> {
const stats: Record<string, number> = {};
this.rooms.forEach((connections, room) => {
stats[room] = connections.size;
});
return stats;
}
}
// 2. Message Types
interface Message {
type: 'join' | 'leave' | 'chat' | 'user_list' | 'room_stats';
data: any;
}
interface ChatMessage {
id: string;
username: string;
content: string;
timestamp: string;
room: string;
}
// 3. Initialize connection manager
const connectionManager = new ConnectionManager();
// 4. Message Handlers
async function handleMessage(connection: Connection, message: Message): Promise<void> {
switch (message.type) {
case 'chat':
await handleChatMessage(connection, message.data);
break;
case 'user_list':
await sendUserList(connection);
break;
case 'room_stats':
await sendRoomStats(connection);
break;
default:
console.log(`Unknown message type: ${message.type}`);
}
}
async function handleChatMessage(connection: Connection, data: { content: string }): Promise<void> {
const chatMessage: ChatMessage = {
id: crypto.randomUUID(),
username: connection.username,
content: data.content,
timestamp: new Date().toISOString(),
room: connection.room,
};
// Validate message
if (!chatMessage.content.trim()) {
connection.ws.send(JSON.stringify({
type: 'error',
data: { message: 'Message content cannot be empty' }
}));
return;
}
// Broadcast to room
connectionManager.broadcastToRoom(connection.room, {
type: 'chat',
data: chatMessage,
});
}
async function sendUserList(connection: Connection): Promise<void> {
const connections = connectionManager.getRoomConnections(connection.room);
const users = connections.map(conn => ({
id: conn.id,
username: conn.username,
joinedAt: conn.joinedAt,
}));
connection.ws.send(JSON.stringify({
type: 'user_list',
data: { users },
}));
}
async function sendRoomStats(connection: Connection): Promise<void> {
const stats = connectionManager.getRoomStats();
const connections = connectionManager.getRoomConnections(connection.room);
connection.ws.send(JSON.stringify({
type: 'room_stats',
data: {
totalUsers: connections.length,
roomStats: stats,
},
}));
}
// 5. WebSocket Handler
async function handleWebSocket(request: Request): Promise<Response> {
const { socket, response } = Deno.upgradeWebSocket(request, {
protocol: "chat-v1",
});
let connection: Connection | null = null;
// Handle WebSocket messages
socket.onopen = () => {
console.log("WebSocket connection opened");
};
socket.onmessage = async (event) => {
try {
const message: Message = JSON.parse(event.data);
if (message.type === 'join') {
// Handle room join
const { room, username } = message.data;
if (!room || !username) {
socket.send(JSON.stringify({
type: 'error',
data: { message: 'Room and username are required' }
}));
return;
}
connection = {
id: crypto.randomUUID(),
ws: socket,
room,
username,
joinedAt: new Date(),
};
connectionManager.addConnection(connection);
// Send join confirmation
socket.send(JSON.stringify({
type: 'join_success',
data: {
roomId: connection.room,
userId: connection.id,
},
}));
// Notify others in room
connectionManager.broadcastToRoom(connection.room, {
type: 'user_joined',
data: {
id: connection.id,
username: connection.username,
},
}, connection.id);
// Send current user list
await sendUserList(connection);
console.log(`User ${username} joined room ${room}`);
} else if (connection) {
// Handle other messages
await handleMessage(connection, message);
}
} catch (error) {
console.error("Error handling WebSocket message:", error);
socket.send(JSON.stringify({
type: 'error',
data: { message: 'Invalid message format' }
}));
}
};
socket.onclose = () => {
if (connection) {
// Notify others in room
connectionManager.broadcastToRoom(connection.room, {
type: 'user_left',
data: {
id: connection.id,
username: connection.username,
},
});
connectionManager.removeConnection(connection.id);
console.log(`WebSocket connection closed for ${connection.username}`);
}
};
socket.onerror = (error) => {
console.error("WebSocket error:", error);
};
return response;
}
// 6. HTTP API Endpoints
async function handleRequest(request: Request): Promise<Response> {
const url = new URL(request.url);
const pathname = url.pathname;
switch (pathname) {
case "/":
return new Response(getHomePage(), {
headers: { "Content-Type": "text/html" },
});
case "/api/stats":
return new Response(
JSON.stringify({
totalConnections: connectionManager.getRoomStats(),
timestamp: new Date().toISOString(),
}),
{
headers: { "Content-Type": "application/json" },
}
);
case "/api/rooms":
return new Response(
JSON.stringify({
rooms: Object.entries(connectionManager.getRoomStats()),
}),
{
headers: { "Content-Type": "application/json" },
}
);
case "/ws":
if (request.headers.get("upgrade") !== "websocket") {
return new Response("Expected websocket connection", { status: 426 });
}
return handleWebSocket(request);
default:
return new Response("Not Found", { status: 404 });
}
}
// 7. Simple HTML Client
function getHomePage(): string {
return `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Deno Deploy WebSocket Chat</title>
<style>
body { font-family: Arial, sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; }
.container { border: 1px solid #ccc; border-radius: 5px; padding: 20px; }
.form-group { margin-bottom: 15px; }
label { display: block; margin-bottom: 5px; font-weight: bold; }
input, select, textarea { width: 100%; padding: 8px; border: 1px solid #ccc; border-radius: 3px; box-sizing: border-box; }
button { background: #007bff; color: white; padding: 10px 20px; border: none; border-radius: 3px; cursor: pointer; }
button:hover { background: #0056b3; }
.chat-container { margin-top: 20px; border: 1px solid #ccc; border-radius: 5px; height: 300px; overflow-y: auto; padding: 10px; }
.message { margin-bottom: 10px; padding: 5px; border-radius: 3px; }
.user-message { background: #e3f2fd; }
.system-message { background: #f5f5f5; font-style: italic; }
.connected { color: green; }
.disconnected { color: red; }
</style>
</head>
<body>
<h1>🦕 Deno Deploy WebSocket Chat</h1>
<div class="container">
<div id="connection-status" class="disconnected">Disconnected</div>
<div id="join-form">
<div class="form-group">
<label for="username">Username:</label>
<input type="text" id="username" placeholder="Enter your username">
</div>
<div class="form-group">
<label for="room">Room:</label>
<select id="room">
<option value="general">General</option>
<option value="tech">Tech Talk</option>
<option value="random">Random</option>
</select>
</div>
<button onclick="joinRoom()">Join Room</button>
</div>
<div id="chat-container" style="display: none;">
<div class="chat-container" id="messages"></div>
<div class="form-group">
<textarea id="message-input" placeholder="Type your message..." rows="3"></textarea>
</div>
<button onclick="sendMessage()">Send Message</button>
<button onclick="leaveRoom()">Leave Room</button>
</div>
</div>
<script>
let ws = null;
let currentUser = null;
let currentRoom = null;
function joinRoom() {
const username = document.getElementById('username').value.trim();
const room = document.getElementById('room').value;
if (!username) {
alert('Please enter a username');
return;
}
currentUser = username;
currentRoom = room;
// Create WebSocket connection
ws = new WebSocket(`${window.location.protocol === 'https:' ? 'wss:' : 'ws:'}//${window.location.host}/ws`);
ws.onopen = function() {
document.getElementById('connection-status').className = 'connected';
document.getElementById('connection-status').textContent = 'Connected';
ws.send(JSON.stringify({
type: 'join',
data: { username, room }
}));
};
ws.onmessage = function(event) {
const message = JSON.parse(event.data);
handleWebSocketMessage(message);
};
ws.onclose = function() {
document.getElementById('connection-status').className = 'disconnected';
document.getElementById('connection-status').textContent = 'Disconnected';
document.getElementById('join-form').style.display = 'block';
document.getElementById('chat-container').style.display = 'none';
};
ws.onerror = function(error) {
console.error('WebSocket error:', error);
alert('Connection error. Please try again.');
};
}
function handleWebSocketMessage(message) {
const messagesContainer = document.getElementById('messages');
switch (message.type) {
case 'join_success':
document.getElementById('join-form').style.display = 'none';
document.getElementById('chat-container').style.display = 'block';
break;
case 'chat':
const chatMessage = message.data;
addMessage(`${chatMessage.username}: ${chatMessage.content}`, 'user-message');
break;
case 'user_joined':
addMessage(`${message.data.username} joined the room`, 'system-message');
break;
case 'user_left':
addMessage(`${message.data.username} left the room`, 'system-message');
break;
case 'error':
addMessage(`Error: ${message.data.message}`, 'system-message');
break;
}
}
function addMessage(content, className = '') {
const messagesContainer = document.getElementById('messages');
const messageDiv = document.createElement('div');
messageDiv.className = `message ${className}`;
messageDiv.textContent = content;
messagesContainer.appendChild(messageDiv);
messagesContainer.scrollTop = messagesContainer.scrollHeight;
}
function sendMessage() {
const input = document.getElementById('message-input');
const content = input.value.trim();
if (content && ws && ws.readyState === WebSocket.OPEN) {
ws.send(JSON.stringify({
type: 'chat',
data: { content }
}));
input.value = '';
}
}
function leaveRoom() {
if (ws) {
ws.close();
}
}
// Handle Enter key in message input
document.getElementById('message-input').addEventListener('keypress', function(e) {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
sendMessage();
}
});
</script>
</body>
</html>
`;
}
// 8. Main Server
export default {
async fetch(request: Request): Promise<Response> {
return handleRequest(request);
},
};
💻 Intégration de Base de Données typescript
🔴 complex
Connecter et opérer des bases de données
// Deno Deploy Database Integration
// database.ts - KV storage, PostgreSQL, and connection management
import { serve } from "https://deno.land/[email protected]/http/server.ts";
// 1. KV Storage (Deno KV)
interface User {
id: string;
username: string;
email: string;
createdAt: string;
updatedAt: string;
}
class KVStorage {
constructor(private kv: Deno.Kv) {}
async setUser(user: User): Promise<void> {
const key = ["users", user.id];
const emailKey = ["users_by_email", user.email];
const entry = {
value: user,
expiration: null, // Never expire
};
await this.kv.set(key, entry);
await this.kv.set(emailKey, { userId: user.id });
}
async getUser(id: string): Promise<User | null> {
const result = await this.kv.get(["users", id]);
return result.value as User | null;
}
async getUserByEmail(email: string): Promise<User | null> {
const result = await this.kv.get(["users_by_email", email]);
if (!result.value) return null;
const userId = (result.value as { userId: string }).userId;
return this.getUser(userId);
}
async updateUser(id: string, updates: Partial<User>): Promise<User | null> {
const user = await this.getUser(id);
if (!user) return null;
const updatedUser = {
...user,
...updates,
updatedAt: new Date().toISOString(),
};
await this.setUser(updatedUser);
return updatedUser;
}
async deleteUser(id: string): Promise<boolean> {
const user = await this.getUser(id);
if (!user) return false;
await this.kv.delete(["users", id]);
await this.kv.delete(["users_by_email", user.email]);
return true;
}
async listUsers(limit: number = 50, cursor?: string): Promise<{ users: User[]; cursor?: string }> {
const iterator = this.kv.list<User>({ prefix: ["users"] }, {
limit,
cursor,
});
const users: User[] = [];
for await (const entry of iterator) {
users.push(entry.value);
}
return {
users,
cursor: iterator.cursor,
};
}
}
// 2. PostgreSQL Connection Pool
interface PostgresConfig {
hostname: string;
port: number;
database: string;
user: string;
password: string;
}
class PostgresConnection {
private connection: any; // postgres.js connection
constructor(private config: PostgresConfig) {}
async connect(): Promise<void> {
// Import postgres.js dynamically
const postgres = await import("https://deno.land/x/[email protected]/mod.js");
this.connection = postgres({
host: this.config.hostname,
port: this.config.port,
database: this.config.database,
user: this.config.user,
password: this.config.password,
ssl: 'require',
max: 10, // Connection pool size
});
// Test connection
await this.connection`SELECT NOW()`;
}
async query<T = any>(sql: string, params?: any[]): Promise<T[]> {
try {
const result = await this.connection.unsafe(sql, params);
return result;
} catch (error) {
console.error("PostgreSQL query error:", error);
throw error;
}
}
async transaction<T>(callback: (sql: any) => Promise<T>): Promise<T> {
return await this.connection.begin(callback);
}
async close(): Promise<void> {
if (this.connection) {
await this.connection.end();
}
}
}
// 3. ORM-like Interface
interface Model {
id: string;
createdAt: string;
updatedAt: string;
}
abstract class BaseModel<T extends Model> {
abstract tableName: string;
constructor(protected db: PostgresConnection) {}
abstract create(data: Omit<T, 'id' | 'createdAt' | 'updatedAt'>): Promise<T>;
abstract findById(id: string): Promise<T | null>;
abstract update(id: string, data: Partial<T>): Promise<T | null>;
abstract delete(id: string): Promise<boolean>;
abstract findAll(limit?: number, offset?: number): Promise<T[]>;
protected generateId(): string {
return crypto.randomUUID();
}
protected now(): string {
return new Date().toISOString();
}
}
// 4. User Model with PostgreSQL
interface Post extends Model {
title: string;
content: string;
authorId: string;
published: boolean;
}
class PostModel extends BaseModel<Post> {
tableName = "posts";
async create(data: Omit<Post, 'id' | 'createdAt' | 'updatedAt'>): Promise<Post> {
const id = this.generateId();
const now = this.now();
const result = await this.db.query<Post[]>(
`INSERT INTO posts (id, title, content, author_id, published, created_at, updated_at)
VALUES ($1, $2, $3, $4, $5, $6, $7)
RETURNING *`,
[id, data.title, data.content, data.authorId, data.published, now, now]
);
return result[0];
}
async findById(id: string): Promise<Post | null> {
const result = await this.db.query<Post[]>(
"SELECT * FROM posts WHERE id = $1",
[id]
);
return result[0] || null;
}
async update(id: string, data: Partial<Post>): Promise<Post | null> {
const now = this.now();
const result = await this.db.query<Post[]>(
`UPDATE posts
SET title = COALESCE($2, title),
content = COALESCE($3, content),
published = COALESCE($4, published),
updated_at = $5
WHERE id = $1
RETURNING *`,
[id, data.title, data.content, data.published, now]
);
return result[0] || null;
}
async delete(id: string): Promise<boolean> {
const result = await this.db.query(
"DELETE FROM posts WHERE id = $1",
[id]
);
return result.count > 0;
}
async findAll(limit: number = 50, offset: number = 0): Promise<Post[]> {
return await this.db.query<Post[]>(
"SELECT * FROM posts ORDER BY created_at DESC LIMIT $1 OFFSET $2",
[limit, offset]
);
}
async findByAuthor(authorId: string): Promise<Post[]> {
return await this.db.query<Post[]>(
"SELECT * FROM posts WHERE author_id = $1 ORDER BY created_at DESC",
[authorId]
);
}
async search(query: string, limit: number = 20): Promise<Post[]> {
return await this.db.query<Post[]>(
`SELECT * FROM posts
WHERE title ILIKE $1 OR content ILIKE $1
ORDER BY created_at DESC
LIMIT $2`,
[`%${query}%`, limit]
);
}
}
// 5. Cache Layer with KV
class CacheLayer {
constructor(private kv: Deno.Kv) {}
private getCacheKey(type: string, id: string): string[] {
return ["cache", type, id];
}
async get<T>(type: string, id: string): Promise<T | null> {
const result = await this.kv.get<T>(this.getCacheKey(type, id));
return result.value;
}
async set<T>(type: string, id: string, value: T, ttl: number = 3600): Promise<void> {
await this.kv.set(this.getCacheKey(type, id), value, { expirationIn: ttl * 1000 });
}
async invalidate(type: string, id: string): Promise<void> {
await this.kv.delete(this.getCacheKey(type, id));
}
async invalidatePattern(pattern: string[]): Promise<void> {
const keys = [];
for await (const entry of this.kv.list({ prefix: ["cache", ...pattern] })) {
keys.push(entry.key);
}
await this.kv.deleteMany(keys);
}
}
// 6. Repository Pattern
class PostRepository {
constructor(
private postModel: PostModel,
private cache: CacheLayer
) {}
async create(data: Omit<Post, 'id' | 'createdAt' | 'updatedAt'>): Promise<Post> {
const post = await this.postModel.create(data);
// Cache the new post
await this.cache.set("posts", post.id, post);
// Invalidate list cache
await this.cache.invalidatePattern(["posts", "list"]);
return post;
}
async findById(id: string, useCache: boolean = true): Promise<Post | null> {
if (useCache) {
const cached = await this.cache.get<Post>("posts", id);
if (cached) return cached;
}
const post = await this.postModel.findById(id);
if (post && useCache) {
await this.cache.set("posts", id, post);
}
return post;
}
async update(id: string, data: Partial<Post>): Promise<Post | null> {
const post = await this.postModel.update(id, data);
if (post) {
// Update cache
await this.cache.set("posts", id, post);
// Invalidate list cache
await this.cache.invalidatePattern(["posts", "list"]);
}
return post;
}
async delete(id: string): Promise<boolean> {
const success = await this.postModel.delete(id);
if (success) {
// Remove from cache
await this.cache.invalidate("posts", id);
// Invalidate list cache
await this.cache.invalidatePattern(["posts", "list"]);
}
return success;
}
async findAll(limit: number = 50, offset: number = 0, useCache: boolean = true): Promise<Post[]> {
const cacheKey = `list_${limit}_${offset}`;
if (useCache) {
const cached = await this.cache.get<Post[]>("posts", cacheKey);
if (cached) return cached;
}
const posts = await this.postModel.findAll(limit, offset);
if (useCache) {
await this.cache.set("posts", cacheKey, posts, 600); // 10 minutes cache
}
return posts;
}
async search(query: string, limit: number = 20): Promise<Post[]> {
const cacheKey = `search_${query}_${limit}`;
// Don't cache search results for long
const cached = await this.cache.get<Post[]>("posts", cacheKey);
if (cached) return cached;
const posts = await this.postModel.search(query, limit);
await this.cache.set("posts", cacheKey, posts, 300); // 5 minutes cache
return posts;
}
}
// 7. Database Initialization
async function initializeDatabase(db: PostgresConnection): Promise<void> {
const migrations = [
`CREATE TABLE IF NOT EXISTS posts (
id UUID PRIMARY KEY,
title VARCHAR(255) NOT NULL,
content TEXT NOT NULL,
author_id UUID NOT NULL,
published BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
)`,
`CREATE INDEX IF NOT EXISTS idx_posts_author_id ON posts(author_id)`,
`CREATE INDEX IF NOT EXISTS idx_posts_created_at ON posts(created_at)`,
`CREATE INDEX IF NOT EXISTS idx_posts_published ON posts(published)`,
`CREATE INDEX IF NOT EXISTS idx_posts_search ON posts USING gin(to_tsvector('english', title || ' ' || content))`,
];
for (const migration of migrations) {
await db.query(migration);
}
console.log("Database initialized successfully");
}
// 8. HTTP API with Database
async function handleRequest(request: Request): Promise<Response> {
const url = new URL(request.url);
const pathname = url.pathname;
const method = request.method;
// Initialize database connections
const kv = await Deno.openKv();
const kvStorage = new KVStorage(kv);
const pgConfig: PostgresConfig = {
hostname: Deno.env.get("DB_HOST") || "localhost",
port: parseInt(Deno.env.get("DB_PORT") || "5432"),
database: Deno.env.get("DB_NAME") || "deno_deploy",
user: Deno.env.get("DB_USER") || "postgres",
password: Deno.env.get("DB_PASSWORD") || "",
};
const postgres = new PostgresConnection(pgConfig);
await postgres.connect();
await initializeDatabase(postgres);
const postModel = new PostModel(postgres);
const cache = new CacheLayer(kv);
const postRepository = new PostRepository(postModel, cache);
try {
// API Routes
if (pathname === "/api/posts" && method === "GET") {
const limit = parseInt(url.searchParams.get("limit") || "50");
const offset = parseInt(url.searchParams.get("offset") || "0");
const posts = await postRepository.findAll(limit, offset);
return new Response(JSON.stringify({ posts, total: posts.length }), {
headers: { "Content-Type": "application/json" },
});
}
if (pathname === "/api/posts" && method === "POST") {
const body = await request.json();
const post = await postRepository.create(body);
return new Response(JSON.stringify(post), {
status: 201,
headers: { "Content-Type": "application/json" },
});
}
if (pathname.startsWith("/api/posts/") && method === "GET") {
const id = pathname.split("/")[3];
const post = await postRepository.findById(id);
if (!post) {
return new Response("Post not found", { status: 404 });
}
return new Response(JSON.stringify(post), {
headers: { "Content-Type": "application/json" },
});
}
// KV Storage examples
if (pathname === "/api/users" && method === "POST") {
const { username, email } = await request.json();
const user: User = {
id: crypto.randomUUID(),
username,
email,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
};
await kvStorage.setUser(user);
return new Response(JSON.stringify(user), {
status: 201,
headers: { "Content-Type": "application/json" },
});
}
return new Response("Not Found", { status: 404 });
} finally {
await postgres.close();
}
}
// 9. Export for Deno Deploy
export default {
async fetch(request: Request): Promise<Response> {
return handleRequest(request);
},
};