Initial commit: Portfolio Website
Vollständige Next.js 15 Portfolio-Website mit: - Blog-System mit 100+ Artikeln - Supabase-Integration - Responsive Design mit Tailwind CSS - TypeScript-Konfiguration - Testing-Setup mit Vitest und Playwright Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,485 @@
|
||||
# Hono: Das ultraschnelle Edge-Framework
|
||||
|
||||
**Meta-Description:** Hono Framework für Edge Computing. Web Standards, Multi-Runtime Support und nur 12KB. Von Cloudflare Workers bis Bun.
|
||||
|
||||
**Keywords:** Hono, Edge Framework, Cloudflare Workers, Web Standards, Bun, Deno, Serverless Framework, TypeScript API
|
||||
|
||||
---
|
||||
|
||||
## Einführung
|
||||
|
||||
Hono (炎 = Flamme) ist ein **ultraschnelles Web-Framework** gebaut auf Web Standards. Mit unter 12KB läuft es auf jedem JavaScript-Runtime: Cloudflare Workers, Deno, Bun, Node.js, AWS Lambda und mehr.
|
||||
|
||||
---
|
||||
|
||||
## Hono Overview
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ HONO FRAMEWORK │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ Core Features: │
|
||||
│ ├── Web Standards API (Request/Response) │
|
||||
│ ├── Zero Dependencies │
|
||||
│ ├── TypeScript First │
|
||||
│ └── < 12KB (hono/tiny) │
|
||||
│ │
|
||||
│ Supported Runtimes: │
|
||||
│ ├── Cloudflare Workers / Pages │
|
||||
│ ├── Deno / Deno Deploy │
|
||||
│ ├── Bun │
|
||||
│ ├── Node.js │
|
||||
│ ├── AWS Lambda │
|
||||
│ ├── Vercel Edge │
|
||||
│ ├── Fastly Compute │
|
||||
│ └── Netlify Edge │
|
||||
│ │
|
||||
│ Built-in Middleware: │
|
||||
│ ├── CORS, CSRF, ETag │
|
||||
│ ├── JWT, Basic Auth, Bearer Auth │
|
||||
│ ├── Logger, Pretty JSON │
|
||||
│ ├── Compress, Cache │
|
||||
│ └── Streaming, SSE │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Quick Start
|
||||
|
||||
```bash
|
||||
# Neues Projekt
|
||||
npm create hono@latest my-app
|
||||
|
||||
# Runtime wählen:
|
||||
# - cloudflare-workers
|
||||
# - cloudflare-pages
|
||||
# - deno
|
||||
# - bun
|
||||
# - nodejs
|
||||
# - vercel
|
||||
|
||||
cd my-app
|
||||
npm install
|
||||
npm run dev
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Basic Routing
|
||||
|
||||
```typescript
|
||||
// src/index.ts
|
||||
import { Hono } from 'hono';
|
||||
|
||||
const app = new Hono();
|
||||
|
||||
// Basic Routes
|
||||
app.get('/', (c) => c.text('Hello Hono!'));
|
||||
|
||||
app.get('/json', (c) => {
|
||||
return c.json({ message: 'Hello', timestamp: Date.now() });
|
||||
});
|
||||
|
||||
// Path Parameters
|
||||
app.get('/users/:id', (c) => {
|
||||
const id = c.req.param('id');
|
||||
return c.json({ userId: id });
|
||||
});
|
||||
|
||||
// Query Parameters
|
||||
app.get('/search', (c) => {
|
||||
const query = c.req.query('q');
|
||||
const page = c.req.query('page') || '1';
|
||||
return c.json({ query, page: parseInt(page) });
|
||||
});
|
||||
|
||||
// POST mit Body
|
||||
app.post('/users', async (c) => {
|
||||
const body = await c.req.json();
|
||||
return c.json({ created: body }, 201);
|
||||
});
|
||||
|
||||
// Wildcards
|
||||
app.get('/files/*', (c) => {
|
||||
const path = c.req.path;
|
||||
return c.text(`File path: ${path}`);
|
||||
});
|
||||
|
||||
export default app;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Typed Routes mit RPC
|
||||
|
||||
```typescript
|
||||
// src/index.ts
|
||||
import { Hono } from 'hono';
|
||||
import { zValidator } from '@hono/zod-validator';
|
||||
import { z } from 'zod';
|
||||
|
||||
const app = new Hono();
|
||||
|
||||
// Schema Definition
|
||||
const createUserSchema = z.object({
|
||||
name: z.string().min(2),
|
||||
email: z.string().email()
|
||||
});
|
||||
|
||||
const userParamsSchema = z.object({
|
||||
id: z.string().uuid()
|
||||
});
|
||||
|
||||
// Typed Route
|
||||
const route = app
|
||||
.get('/users', (c) => {
|
||||
return c.json([
|
||||
{ id: '1', name: 'Alice' },
|
||||
{ id: '2', name: 'Bob' }
|
||||
]);
|
||||
})
|
||||
.post(
|
||||
'/users',
|
||||
zValidator('json', createUserSchema),
|
||||
(c) => {
|
||||
const data = c.req.valid('json');
|
||||
// data ist typisiert: { name: string; email: string }
|
||||
return c.json({ id: crypto.randomUUID(), ...data }, 201);
|
||||
}
|
||||
)
|
||||
.get(
|
||||
'/users/:id',
|
||||
zValidator('param', userParamsSchema),
|
||||
(c) => {
|
||||
const { id } = c.req.valid('param');
|
||||
return c.json({ id, name: 'Alice' });
|
||||
}
|
||||
);
|
||||
|
||||
// Type Export für Client
|
||||
export type AppType = typeof route;
|
||||
|
||||
export default app;
|
||||
```
|
||||
|
||||
```typescript
|
||||
// Client-Side (hc = Hono Client)
|
||||
import { hc } from 'hono/client';
|
||||
import type { AppType } from './server';
|
||||
|
||||
const client = hc<AppType>('http://localhost:8787');
|
||||
|
||||
// Vollständig typisiert!
|
||||
const users = await client.users.$get();
|
||||
const newUser = await client.users.$post({
|
||||
json: { name: 'Charlie', email: 'charlie@example.com' }
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Middleware
|
||||
|
||||
```typescript
|
||||
import { Hono } from 'hono';
|
||||
import { cors } from 'hono/cors';
|
||||
import { jwt } from 'hono/jwt';
|
||||
import { logger } from 'hono/logger';
|
||||
import { prettyJSON } from 'hono/pretty-json';
|
||||
import { secureHeaders } from 'hono/secure-headers';
|
||||
import { compress } from 'hono/compress';
|
||||
|
||||
const app = new Hono();
|
||||
|
||||
// Global Middleware
|
||||
app.use('*', logger());
|
||||
app.use('*', secureHeaders());
|
||||
app.use('*', compress());
|
||||
|
||||
// CORS für API Routes
|
||||
app.use('/api/*', cors({
|
||||
origin: ['https://example.com', 'https://app.example.com'],
|
||||
allowMethods: ['GET', 'POST', 'PUT', 'DELETE'],
|
||||
allowHeaders: ['Content-Type', 'Authorization'],
|
||||
credentials: true
|
||||
}));
|
||||
|
||||
// JWT für geschützte Routes
|
||||
app.use('/api/protected/*', jwt({
|
||||
secret: process.env.JWT_SECRET!
|
||||
}));
|
||||
|
||||
// Pretty JSON in Development
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
app.use('*', prettyJSON());
|
||||
}
|
||||
|
||||
// Custom Middleware
|
||||
const timing = () => {
|
||||
return async (c, next) => {
|
||||
const start = Date.now();
|
||||
await next();
|
||||
const duration = Date.now() - start;
|
||||
c.header('X-Response-Time', `${duration}ms`);
|
||||
};
|
||||
};
|
||||
|
||||
app.use('*', timing());
|
||||
|
||||
// Protected Route
|
||||
app.get('/api/protected/me', (c) => {
|
||||
const payload = c.get('jwtPayload');
|
||||
return c.json({ user: payload });
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Error Handling
|
||||
|
||||
```typescript
|
||||
import { Hono } from 'hono';
|
||||
import { HTTPException } from 'hono/http-exception';
|
||||
|
||||
const app = new Hono();
|
||||
|
||||
// Custom Error Handler
|
||||
app.onError((err, c) => {
|
||||
if (err instanceof HTTPException) {
|
||||
return c.json(
|
||||
{ error: err.message, status: err.status },
|
||||
err.status
|
||||
);
|
||||
}
|
||||
|
||||
console.error(err);
|
||||
return c.json(
|
||||
{ error: 'Internal Server Error', status: 500 },
|
||||
500
|
||||
);
|
||||
});
|
||||
|
||||
// Not Found Handler
|
||||
app.notFound((c) => {
|
||||
return c.json({ error: 'Not Found', status: 404 }, 404);
|
||||
});
|
||||
|
||||
// Throwing Errors
|
||||
app.get('/users/:id', async (c) => {
|
||||
const user = await getUser(c.req.param('id'));
|
||||
|
||||
if (!user) {
|
||||
throw new HTTPException(404, { message: 'User not found' });
|
||||
}
|
||||
|
||||
return c.json(user);
|
||||
});
|
||||
|
||||
// Mit Custom Response
|
||||
app.get('/admin', (c) => {
|
||||
const isAdmin = checkAdmin(c);
|
||||
|
||||
if (!isAdmin) {
|
||||
throw new HTTPException(403, {
|
||||
message: 'Forbidden',
|
||||
res: c.json({ error: 'Admin access required' }, 403)
|
||||
});
|
||||
}
|
||||
|
||||
return c.json({ admin: true });
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Cloudflare Workers Integration
|
||||
|
||||
```typescript
|
||||
// src/index.ts
|
||||
import { Hono } from 'hono';
|
||||
|
||||
type Bindings = {
|
||||
DB: D1Database;
|
||||
KV: KVNamespace;
|
||||
AI: Ai;
|
||||
BUCKET: R2Bucket;
|
||||
};
|
||||
|
||||
const app = new Hono<{ Bindings: Bindings }>();
|
||||
|
||||
// D1 Database
|
||||
app.get('/users', async (c) => {
|
||||
const { results } = await c.env.DB
|
||||
.prepare('SELECT * FROM users LIMIT 10')
|
||||
.all();
|
||||
return c.json(results);
|
||||
});
|
||||
|
||||
// KV Storage
|
||||
app.get('/cache/:key', async (c) => {
|
||||
const key = c.req.param('key');
|
||||
const value = await c.env.KV.get(key);
|
||||
|
||||
if (!value) {
|
||||
return c.json({ error: 'Not found' }, 404);
|
||||
}
|
||||
|
||||
return c.json({ key, value: JSON.parse(value) });
|
||||
});
|
||||
|
||||
app.put('/cache/:key', async (c) => {
|
||||
const key = c.req.param('key');
|
||||
const body = await c.req.json();
|
||||
|
||||
await c.env.KV.put(key, JSON.stringify(body), {
|
||||
expirationTtl: 3600 // 1 Stunde
|
||||
});
|
||||
|
||||
return c.json({ success: true });
|
||||
});
|
||||
|
||||
// R2 Storage
|
||||
app.post('/upload', async (c) => {
|
||||
const formData = await c.req.formData();
|
||||
const file = formData.get('file') as File;
|
||||
|
||||
await c.env.BUCKET.put(file.name, file);
|
||||
|
||||
return c.json({ uploaded: file.name });
|
||||
});
|
||||
|
||||
// Workers AI
|
||||
app.post('/ai/chat', async (c) => {
|
||||
const { message } = await c.req.json();
|
||||
|
||||
const response = await c.env.AI.run('@cf/meta/llama-2-7b-chat-int8', {
|
||||
messages: [{ role: 'user', content: message }]
|
||||
});
|
||||
|
||||
return c.json(response);
|
||||
});
|
||||
|
||||
export default app;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Streaming & SSE
|
||||
|
||||
```typescript
|
||||
import { Hono } from 'hono';
|
||||
import { streamSSE, streamText } from 'hono/streaming';
|
||||
|
||||
const app = new Hono();
|
||||
|
||||
// Server-Sent Events
|
||||
app.get('/sse', (c) => {
|
||||
return streamSSE(c, async (stream) => {
|
||||
let id = 0;
|
||||
|
||||
while (true) {
|
||||
await stream.writeSSE({
|
||||
data: JSON.stringify({ time: new Date().toISOString() }),
|
||||
event: 'tick',
|
||||
id: String(id++)
|
||||
});
|
||||
|
||||
await stream.sleep(1000);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Text Streaming (für AI)
|
||||
app.get('/stream', (c) => {
|
||||
return streamText(c, async (stream) => {
|
||||
const words = 'Hello, this is a streaming response!'.split(' ');
|
||||
|
||||
for (const word of words) {
|
||||
await stream.write(word + ' ');
|
||||
await stream.sleep(100);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// AI Chat Streaming
|
||||
app.post('/chat', async (c) => {
|
||||
const { message } = await c.req.json();
|
||||
|
||||
return streamText(c, async (stream) => {
|
||||
const response = await openai.chat.completions.create({
|
||||
model: 'gpt-4',
|
||||
messages: [{ role: 'user', content: message }],
|
||||
stream: true
|
||||
});
|
||||
|
||||
for await (const chunk of response) {
|
||||
const content = chunk.choices[0]?.delta?.content;
|
||||
if (content) {
|
||||
await stream.write(content);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## HonoX (Meta-Framework)
|
||||
|
||||
```typescript
|
||||
// HonoX: File-based Routing + Islands Architecture
|
||||
|
||||
// app/routes/index.tsx
|
||||
export default function Home() {
|
||||
return (
|
||||
<html>
|
||||
<body>
|
||||
<h1>Welcome to HonoX</h1>
|
||||
<Counter /> {/* Client Island */}
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
|
||||
// app/routes/api/users.ts
|
||||
import { Hono } from 'hono';
|
||||
|
||||
const app = new Hono();
|
||||
|
||||
app.get('/', (c) => c.json({ users: [] }));
|
||||
|
||||
export default app;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Fazit
|
||||
|
||||
Hono bietet:
|
||||
|
||||
1. **Ultra-Lightweight**: < 12KB, zero dependencies
|
||||
2. **Multi-Runtime**: Gleicher Code überall
|
||||
3. **Web Standards**: Request/Response API
|
||||
4. **Type-Safe RPC**: End-to-End TypeScript
|
||||
|
||||
Das ideale Framework für Edge-First Entwicklung.
|
||||
|
||||
---
|
||||
|
||||
## Bildprompts
|
||||
|
||||
1. "Flame icon transforming into web API endpoints, Hono framework concept"
|
||||
2. "Code running across multiple cloud platforms, multi-runtime visualization"
|
||||
3. "Lightweight feather compared to heavy framework boxes, minimal bundle"
|
||||
|
||||
---
|
||||
|
||||
## Quellen
|
||||
|
||||
- [Hono Documentation](https://hono.dev/)
|
||||
- [Hono GitHub](https://github.com/honojs/hono)
|
||||
- [Cloudflare Hono Guide](https://developers.cloudflare.com/workers/framework-guides/web-apps/more-web-frameworks/hono/)
|
||||
- [The Story of Hono](https://blog.cloudflare.com/the-story-of-web-framework-hono-from-the-creator-of-hono/)
|
||||
Reference in New Issue
Block a user