@objectstack/plugin-hono-server 0.6.1 â 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +10 -0
- package/README.md +324 -0
- package/dist/adapter.js +22 -8
- package/dist/hono-plugin.js +277 -24
- package/objectstack.config.ts +238 -0
- package/package.json +4 -4
- package/src/adapter.ts +20 -6
- package/src/hono-plugin.ts +305 -32
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# @objectstack/plugin-hono-server
|
|
2
2
|
|
|
3
|
+
## 0.7.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Patch release for maintenance and stability improvements
|
|
8
|
+
- Updated dependencies
|
|
9
|
+
- @objectstack/spec@0.7.1
|
|
10
|
+
- @objectstack/types@0.7.1
|
|
11
|
+
- @objectstack/core@0.7.1
|
|
12
|
+
|
|
3
13
|
## 0.6.1
|
|
4
14
|
|
|
5
15
|
### Patch Changes
|
package/README.md
ADDED
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
# @objectstack/plugin-hono-server
|
|
2
|
+
|
|
3
|
+
HTTP Server Adapter for ObjectStack Runtime using the [Hono](https://hono.dev/) framework. This plugin provides a production-ready REST API gateway for ObjectStack applications.
|
|
4
|
+
|
|
5
|
+
## Plugin Capabilities
|
|
6
|
+
|
|
7
|
+
This plugin implements the ObjectStack plugin capability protocol:
|
|
8
|
+
- **Type**: `adapter`
|
|
9
|
+
- **Protocol**: `com.objectstack.protocol.http.v1` (full conformance)
|
|
10
|
+
- **Protocol**: `com.objectstack.protocol.api.rest.v1` (full conformance)
|
|
11
|
+
- **Provides**: `IHttpServer` interface for HTTP server operations
|
|
12
|
+
- **Requires**: `com.objectstack.engine.objectql` (optional) for protocol implementation
|
|
13
|
+
- **Extension Points**:
|
|
14
|
+
- `middleware` - Register custom HTTP middleware
|
|
15
|
+
- `route` - Register custom API routes
|
|
16
|
+
|
|
17
|
+
See [objectstack.config.ts](./objectstack.config.ts) for the complete capability manifest.
|
|
18
|
+
|
|
19
|
+
## Features
|
|
20
|
+
|
|
21
|
+
- ð **High Performance**: Built on Hono, one of the fastest web frameworks
|
|
22
|
+
- ð **Universal**: Works in Node.js, Deno, Bun, and edge runtimes
|
|
23
|
+
- ð **Type Safe**: Fully typed with TypeScript
|
|
24
|
+
- ðĄ **REST API**: Complete ObjectStack Runtime Protocol implementation
|
|
25
|
+
- ðŊ **Auto-Discovery**: Automatic endpoint registration
|
|
26
|
+
- ð **Extensible**: Easy to add custom routes and middleware
|
|
27
|
+
|
|
28
|
+
## Installation
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
pnpm add @objectstack/plugin-hono-server hono @hono/node-server
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Usage
|
|
35
|
+
|
|
36
|
+
### Basic Setup
|
|
37
|
+
|
|
38
|
+
```typescript
|
|
39
|
+
import { HonoServerPlugin } from '@objectstack/plugin-hono-server';
|
|
40
|
+
import { ObjectKernel } from '@objectstack/runtime';
|
|
41
|
+
|
|
42
|
+
const kernel = new ObjectKernel();
|
|
43
|
+
|
|
44
|
+
// Register the server plugin
|
|
45
|
+
kernel.use(new HonoServerPlugin({
|
|
46
|
+
port: 3000,
|
|
47
|
+
staticRoot: './public' // Optional: serve static files
|
|
48
|
+
}));
|
|
49
|
+
|
|
50
|
+
await kernel.bootstrap();
|
|
51
|
+
|
|
52
|
+
// Server starts automatically when kernel is ready
|
|
53
|
+
// API available at: http://localhost:3000/api/v1
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### With Custom Port
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
const plugin = new HonoServerPlugin({
|
|
60
|
+
port: process.env.PORT || 8080
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
kernel.use(plugin);
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Configuration Options
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
interface HonoPluginOptions {
|
|
70
|
+
/**
|
|
71
|
+
* HTTP server port
|
|
72
|
+
* @default 3000
|
|
73
|
+
*/
|
|
74
|
+
port?: number;
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Path to static files directory (optional)
|
|
78
|
+
*/
|
|
79
|
+
staticRoot?: string;
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## API Endpoints
|
|
84
|
+
|
|
85
|
+
The plugin automatically exposes the following ObjectStack REST API endpoints:
|
|
86
|
+
|
|
87
|
+
### Discovery
|
|
88
|
+
|
|
89
|
+
```http
|
|
90
|
+
GET /api/v1
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Returns API discovery information including available endpoints and versions.
|
|
94
|
+
|
|
95
|
+
### Metadata Protocol
|
|
96
|
+
|
|
97
|
+
```http
|
|
98
|
+
GET /api/v1/meta
|
|
99
|
+
GET /api/v1/meta/:type
|
|
100
|
+
GET /api/v1/meta/:type/:name
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Retrieve metadata about objects, views, and other system definitions.
|
|
104
|
+
|
|
105
|
+
### Data Protocol (CRUD Operations)
|
|
106
|
+
|
|
107
|
+
```http
|
|
108
|
+
GET /api/v1/data/:object # Find records
|
|
109
|
+
GET /api/v1/data/:object/:id # Get record by ID
|
|
110
|
+
POST /api/v1/data/:object # Create record
|
|
111
|
+
PATCH /api/v1/data/:object/:id # Update record
|
|
112
|
+
DELETE /api/v1/data/:object/:id # Delete record
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Example requests:
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
# Get all users
|
|
119
|
+
curl http://localhost:3000/api/v1/data/user
|
|
120
|
+
|
|
121
|
+
# Get user by ID
|
|
122
|
+
curl http://localhost:3000/api/v1/data/user/123
|
|
123
|
+
|
|
124
|
+
# Create a user
|
|
125
|
+
curl -X POST http://localhost:3000/api/v1/data/user \
|
|
126
|
+
-H "Content-Type: application/json" \
|
|
127
|
+
-d '{"name":"John Doe","email":"john@example.com"}'
|
|
128
|
+
|
|
129
|
+
# Update a user
|
|
130
|
+
curl -X PATCH http://localhost:3000/api/v1/data/user/123 \
|
|
131
|
+
-H "Content-Type: application/json" \
|
|
132
|
+
-d '{"name":"Jane Doe"}'
|
|
133
|
+
|
|
134
|
+
# Delete a user
|
|
135
|
+
curl -X DELETE http://localhost:3000/api/v1/data/user/123
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### UI Protocol
|
|
139
|
+
|
|
140
|
+
```http
|
|
141
|
+
GET /api/v1/ui/view/:object?type=list|form
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Retrieve UI view configurations for objects.
|
|
145
|
+
|
|
146
|
+
## Advanced Usage
|
|
147
|
+
|
|
148
|
+
### Accessing the HTTP Server Instance
|
|
149
|
+
|
|
150
|
+
The server instance is registered as a service and can be accessed by other plugins:
|
|
151
|
+
|
|
152
|
+
```typescript
|
|
153
|
+
export class MyPlugin implements Plugin {
|
|
154
|
+
name = 'my-custom-plugin';
|
|
155
|
+
|
|
156
|
+
async start(ctx: PluginContext) {
|
|
157
|
+
const httpServer = ctx.getService<IHttpServer>('http-server');
|
|
158
|
+
|
|
159
|
+
// Add custom routes
|
|
160
|
+
httpServer.get('/api/custom', (req, res) => {
|
|
161
|
+
res.json({ message: 'Custom endpoint' });
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### Extending with Middleware
|
|
168
|
+
|
|
169
|
+
The plugin provides extension points for adding custom middleware:
|
|
170
|
+
|
|
171
|
+
```typescript
|
|
172
|
+
// In another plugin's manifest
|
|
173
|
+
capabilities: {
|
|
174
|
+
extensions: [
|
|
175
|
+
{
|
|
176
|
+
targetPluginId: 'com.objectstack.server.hono',
|
|
177
|
+
extensionPointId: 'com.objectstack.server.hono.extension.middleware',
|
|
178
|
+
implementation: './middleware/auth.ts',
|
|
179
|
+
priority: 10
|
|
180
|
+
}
|
|
181
|
+
]
|
|
182
|
+
}
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### Custom Route Registration
|
|
186
|
+
|
|
187
|
+
```typescript
|
|
188
|
+
// In another plugin's manifest
|
|
189
|
+
capabilities: {
|
|
190
|
+
extensions: [
|
|
191
|
+
{
|
|
192
|
+
targetPluginId: 'com.objectstack.server.hono',
|
|
193
|
+
extensionPointId: 'com.objectstack.server.hono.extension.route',
|
|
194
|
+
implementation: './routes/webhooks.ts',
|
|
195
|
+
priority: 50
|
|
196
|
+
}
|
|
197
|
+
]
|
|
198
|
+
}
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
## Architecture
|
|
202
|
+
|
|
203
|
+
The Hono Server Plugin follows a clean architecture:
|
|
204
|
+
|
|
205
|
+
```
|
|
206
|
+
âââââââââââââââââââââââââââââââââââ
|
|
207
|
+
â HonoServerPlugin â
|
|
208
|
+
â (Plugin Lifecycle) â
|
|
209
|
+
ââââââââââââââŽâââââââââââââââââââââ
|
|
210
|
+
â
|
|
211
|
+
ââ init() â Register HTTP server service
|
|
212
|
+
ââ start() â Bind routes, start server
|
|
213
|
+
ââ destroy() â Stop server
|
|
214
|
+
â
|
|
215
|
+
âž
|
|
216
|
+
âââââââââââââââââââââââ
|
|
217
|
+
â HonoHttpServer â
|
|
218
|
+
â (Adapter) â
|
|
219
|
+
ââââââââŽâââââââââââââââ
|
|
220
|
+
â
|
|
221
|
+
âž
|
|
222
|
+
âââââââââââââââââââââââ
|
|
223
|
+
â Hono Framework â
|
|
224
|
+
â (Core Library) â
|
|
225
|
+
âââââââââââââââââââââââ
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
## Plugin Lifecycle
|
|
229
|
+
|
|
230
|
+
1. **Init Phase**:
|
|
231
|
+
- Creates HonoHttpServer instance
|
|
232
|
+
- Registers as `http-server` service
|
|
233
|
+
|
|
234
|
+
2. **Start Phase**:
|
|
235
|
+
- Retrieves protocol implementation service
|
|
236
|
+
- Registers all ObjectStack API routes
|
|
237
|
+
- Sets up lifecycle hooks
|
|
238
|
+
|
|
239
|
+
3. **Ready Hook** (`kernel:ready`):
|
|
240
|
+
- Starts HTTP server on configured port
|
|
241
|
+
- Logs server URL
|
|
242
|
+
|
|
243
|
+
4. **Destroy Phase**:
|
|
244
|
+
- Gracefully closes server
|
|
245
|
+
- Cleans up resources
|
|
246
|
+
|
|
247
|
+
## Error Handling
|
|
248
|
+
|
|
249
|
+
The plugin includes comprehensive error handling:
|
|
250
|
+
|
|
251
|
+
```typescript
|
|
252
|
+
// 404 Not Found
|
|
253
|
+
GET /api/v1/data/user/999
|
|
254
|
+
â { "error": "Record not found" }
|
|
255
|
+
|
|
256
|
+
// 400 Bad Request
|
|
257
|
+
POST /api/v1/data/user (invalid data)
|
|
258
|
+
â { "error": "Validation failed: email is required" }
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
## Production Deployment
|
|
262
|
+
|
|
263
|
+
### Environment Variables
|
|
264
|
+
|
|
265
|
+
```bash
|
|
266
|
+
PORT=8080
|
|
267
|
+
NODE_ENV=production
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
### Docker Example
|
|
271
|
+
|
|
272
|
+
```dockerfile
|
|
273
|
+
FROM node:20-alpine
|
|
274
|
+
WORKDIR /app
|
|
275
|
+
COPY package.json pnpm-lock.yaml ./
|
|
276
|
+
RUN npm install -g pnpm && pnpm install --frozen-lockfile
|
|
277
|
+
COPY . .
|
|
278
|
+
RUN pnpm build
|
|
279
|
+
EXPOSE 8080
|
|
280
|
+
CMD ["node", "dist/index.js"]
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
### Serverless Deployment
|
|
284
|
+
|
|
285
|
+
Hono works seamlessly with serverless platforms:
|
|
286
|
+
|
|
287
|
+
```typescript
|
|
288
|
+
// Cloudflare Workers, Vercel Edge, etc.
|
|
289
|
+
export default {
|
|
290
|
+
async fetch(request: Request) {
|
|
291
|
+
const app = createHonoApp();
|
|
292
|
+
return app.fetch(request);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
## Performance
|
|
298
|
+
|
|
299
|
+
Hono is designed for performance:
|
|
300
|
+
- ⥠One of the fastest web frameworks for Node.js
|
|
301
|
+
- ðŠķ Minimal overhead and memory footprint
|
|
302
|
+
- ð Optimized routing with RegExpRouter
|
|
303
|
+
- ðĶ Small bundle size (~12KB)
|
|
304
|
+
|
|
305
|
+
## Comparison with Other Adapters
|
|
306
|
+
|
|
307
|
+
| Feature | Hono | Express | Fastify |
|
|
308
|
+
|---------|------|---------|---------|
|
|
309
|
+
| Universal Runtime | â
| â | â |
|
|
310
|
+
| Edge Support | â
| â | â |
|
|
311
|
+
| TypeScript | â
| Partial | â
|
|
|
312
|
+
| Performance | Excellent | Good | Excellent |
|
|
313
|
+
| Bundle Size | 12KB | 208KB | 28KB |
|
|
314
|
+
|
|
315
|
+
## License
|
|
316
|
+
|
|
317
|
+
Apache-2.0
|
|
318
|
+
|
|
319
|
+
## Related Packages
|
|
320
|
+
|
|
321
|
+
- [@objectstack/runtime](../../runtime) - ObjectStack Runtime
|
|
322
|
+
- [@objectstack/spec](../../spec) - ObjectStack Specifications
|
|
323
|
+
- [hono](https://hono.dev/) - Hono Web Framework
|
|
324
|
+
- [@hono/node-server](https://github.com/honojs/node-server) - Node.js adapter for Hono
|
package/dist/adapter.js
CHANGED
|
@@ -36,21 +36,35 @@ class HonoHttpServer {
|
|
|
36
36
|
// internal helper to convert standard handler to Hono handler
|
|
37
37
|
wrap(handler) {
|
|
38
38
|
return async (c) => {
|
|
39
|
+
let body = {};
|
|
40
|
+
// Try to parse JSON body first if content-type is JSON
|
|
41
|
+
if (c.req.header('content-type')?.includes('application/json')) {
|
|
42
|
+
try {
|
|
43
|
+
body = await c.req.json();
|
|
44
|
+
}
|
|
45
|
+
catch (e) {
|
|
46
|
+
// If JSON parsing fails, try parseBody
|
|
47
|
+
try {
|
|
48
|
+
body = await c.req.parseBody();
|
|
49
|
+
}
|
|
50
|
+
catch (e2) { }
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
// For non-JSON content types, use parseBody
|
|
55
|
+
try {
|
|
56
|
+
body = await c.req.parseBody();
|
|
57
|
+
}
|
|
58
|
+
catch (e) { }
|
|
59
|
+
}
|
|
39
60
|
const req = {
|
|
40
61
|
params: c.req.param(),
|
|
41
62
|
query: c.req.query(),
|
|
42
|
-
body
|
|
63
|
+
body,
|
|
43
64
|
headers: c.req.header(),
|
|
44
65
|
method: c.req.method,
|
|
45
66
|
path: c.req.path
|
|
46
67
|
};
|
|
47
|
-
// Try to parse JSON body if possible
|
|
48
|
-
if (c.req.header('content-type')?.includes('application/json')) {
|
|
49
|
-
try {
|
|
50
|
-
req.body = await c.req.json();
|
|
51
|
-
}
|
|
52
|
-
catch (e) { }
|
|
53
|
-
}
|
|
54
68
|
let capturedResponse;
|
|
55
69
|
const res = {
|
|
56
70
|
json: (data) => { capturedResponse = c.json(data); },
|