or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

cli-interface.mddependency-injection.mdevent-system.mdhttp-interface.mdindex.mdrpc-communication.mdservice-management.mdstandalone-clients.mdtesting-framework.mdtimer-scheduling.md

http-interface.mddocs/

0

# HTTP Interface

1

2

HTTP server integration providing REST API endpoints with request/response handling, URL routing, WebSocket support, and integration with external web frameworks.

3

4

## Capabilities

5

6

### HTTP Endpoint Decorator

7

8

Decorator that exposes service methods as HTTP endpoints with flexible routing, method handling, and middleware support.

9

10

```python { .api }

11

def http(method, url, **kwargs):

12

"""

13

Decorator to expose a service method as an HTTP endpoint.

14

15

Parameters:

16

- method: HTTP method ('GET', 'POST', 'PUT', 'DELETE', etc.)

17

- url: URL pattern with optional path parameters

18

- expected_exceptions: Tuple of exceptions to convert to HTTP errors

19

- cors: CORS configuration dictionary

20

21

Returns:

22

Decorated method that handles HTTP requests

23

"""

24

```

25

26

**Usage Example:**

27

28

```python

29

from nameko.web.handlers import http

30

from nameko.rpc import RpcProxy

31

import json

32

33

class UserAPIService:

34

name = "user_api"

35

36

user_rpc = RpcProxy('user_service')

37

38

@http('GET', '/users/<int:user_id>')

39

def get_user(self, request, user_id):

40

"""Get user by ID"""

41

try:

42

user = self.user_rpc.get_user(user_id)

43

return json.dumps(user)

44

except Exception as e:

45

return {'error': str(e)}, 404

46

47

@http('POST', '/users')

48

def create_user(self, request):

49

"""Create a new user"""

50

user_data = json.loads(request.get_data())

51

52

# Validate required fields

53

if not user_data.get('email'):

54

return {'error': 'Email is required'}, 400

55

56

user = self.user_rpc.create_user(user_data)

57

return json.dumps(user), 201

58

59

@http('PUT', '/users/<int:user_id>')

60

def update_user(self, request, user_id):

61

"""Update existing user"""

62

user_data = json.loads(request.get_data())

63

user = self.user_rpc.update_user(user_id, user_data)

64

return json.dumps(user)

65

66

@http('DELETE', '/users/<int:user_id>')

67

def delete_user(self, request, user_id):

68

"""Delete user"""

69

self.user_rpc.delete_user(user_id)

70

return '', 204

71

```

72

73

### Request Object

74

75

HTTP handlers receive a request object containing all HTTP request information.

76

77

```python { .api }

78

# Request object attributes and methods

79

class Request:

80

"""

81

HTTP request object passed to HTTP handlers.

82

83

Attributes:

84

- method: HTTP method (GET, POST, etc.)

85

- url: Request URL

86

- headers: Request headers dictionary

87

- args: Query string parameters

88

- form: Form data for POST requests

89

- json: Parsed JSON data

90

- files: Uploaded files

91

- cookies: Request cookies

92

"""

93

94

def get_data(self, as_text=False):

95

"""Get raw request body data"""

96

97

def get_json(self):

98

"""Parse request body as JSON"""

99

```

100

101

**Request Handling Example:**

102

103

```python

104

@http('POST', '/api/upload')

105

def handle_upload(self, request):

106

# Access different request components

107

content_type = request.headers.get('Content-Type')

108

query_params = request.args

109

110

if content_type == 'application/json':

111

data = request.get_json()

112

return self._process_json(data)

113

elif 'multipart/form-data' in content_type:

114

files = request.files

115

form_data = request.form

116

return self._process_upload(files, form_data)

117

else:

118

raw_data = request.get_data(as_text=True)

119

return self._process_raw(raw_data)

120

```

121

122

### Response Handling

123

124

HTTP handlers can return various response formats with status codes and headers.

125

126

**Response Format Options:**

127

128

```python

129

@http('GET', '/api/data')

130

def get_data(self, request):

131

# Return string

132

return "Hello World"

133

134

# Return JSON (auto-serialized)

135

return {'message': 'Hello World'}

136

137

# Return with status code

138

return {'error': 'Not found'}, 404

139

140

# Return with status code and headers

141

return (

142

{'data': 'value'},

143

200,

144

{'Content-Type': 'application/json', 'X-Custom': 'header'}

145

)

146

147

# Return Response object for full control

148

from werkzeug.wrappers import Response

149

return Response(

150

json.dumps({'data': 'value'}),

151

status=200,

152

headers={'Content-Type': 'application/json'}

153

)

154

```

155

156

### URL Routing and Parameters

157

158

Flexible URL routing with path parameters, query parameters, and pattern matching.

159

160

```python

161

class APIService:

162

name = "api_service"

163

164

# Path parameters with type conversion

165

@http('GET', '/users/<int:user_id>')

166

def get_user(self, request, user_id):

167

# user_id is automatically converted to int

168

pass

169

170

# Multiple path parameters

171

@http('GET', '/users/<int:user_id>/posts/<int:post_id>')

172

def get_user_post(self, request, user_id, post_id):

173

pass

174

175

# String parameters (default)

176

@http('GET', '/categories/<category_name>')

177

def get_category(self, request, category_name):

178

pass

179

180

# UUID parameters

181

@http('GET', '/orders/<uuid:order_id>')

182

def get_order(self, request, order_id):

183

pass

184

185

# Query parameters

186

@http('GET', '/search')

187

def search(self, request):

188

query = request.args.get('q', '')

189

limit = int(request.args.get('limit', 10))

190

offset = int(request.args.get('offset', 0))

191

return self._search(query, limit, offset)

192

```

193

194

### CORS Support

195

196

Cross-Origin Resource Sharing (CORS) configuration for browser-based applications.

197

198

```python { .api }

199

@http('GET', '/api/data', cors={

200

'origins': ['http://localhost:3000', 'https://myapp.com'],

201

'methods': ['GET', 'POST', 'PUT', 'DELETE'],

202

'headers': ['Content-Type', 'Authorization'],

203

'credentials': True

204

})

205

def api_endpoint(self, request):

206

"""Endpoint with CORS configuration"""

207

```

208

209

### WebSocket Support

210

211

WebSocket RPC endpoints for real-time bidirectional communication with connection management and room-based messaging.

212

213

```python { .api }

214

from nameko.web.websocket import rpc as websocket_rpc, WebSocketHubProvider

215

216

@websocket_rpc

217

def websocket_method(socket_id, *args, **kwargs):

218

"""

219

Decorator for WebSocket RPC methods.

220

221

Parameters:

222

- socket_id: Unique identifier for the WebSocket connection

223

- *args, **kwargs: Method arguments

224

225

Returns:

226

Method result sent back to the WebSocket client

227

"""

228

229

class WebSocketHubProvider:

230

"""

231

Dependency provider for WebSocket hub functionality.

232

Manages WebSocket connections, rooms, and messaging.

233

"""

234

235

class WebSocketHub:

236

"""

237

Hub for managing WebSocket connections and broadcasting messages.

238

"""

239

240

def subscribe(self, socket_id, channel):

241

"""

242

Subscribe a WebSocket connection to a channel.

243

244

Parameters:

245

- socket_id: WebSocket connection identifier

246

- channel: Channel name to subscribe to

247

"""

248

249

def unsubscribe(self, socket_id, channel):

250

"""

251

Unsubscribe a WebSocket connection from a channel.

252

253

Parameters:

254

- socket_id: WebSocket connection identifier

255

- channel: Channel name to unsubscribe from

256

"""

257

258

def broadcast(self, channel, event, data):

259

"""

260

Broadcast a message to all connections subscribed to a channel.

261

262

Parameters:

263

- channel: Channel name to broadcast to

264

- event: Event name/type

265

- data: Message data to send

266

"""

267

268

def unicast(self, socket_id, event, data):

269

"""

270

Send a message to a specific WebSocket connection.

271

272

Parameters:

273

- socket_id: Target WebSocket connection identifier

274

- event: Event name/type

275

- data: Message data to send

276

"""

277

278

def get_subscriptions(self, socket_id):

279

"""

280

Get list of channels a WebSocket connection is subscribed to.

281

282

Parameters:

283

- socket_id: WebSocket connection identifier

284

285

Returns:

286

List of channel names

287

"""

288

```

289

290

**Usage Example:**

291

292

```python

293

from nameko.web.websocket import rpc as websocket_rpc, WebSocketHubProvider

294

from nameko.exceptions import ConnectionNotFound

295

296

class ChatService:

297

name = "chat_service"

298

299

websocket_hub = WebSocketHubProvider()

300

301

@websocket_rpc

302

def join_room(self, socket_id, room_id):

303

"""Subscribe socket to a chat room"""

304

self.websocket_hub.subscribe(socket_id, f'room:{room_id}')

305

306

# Notify other room members

307

self.websocket_hub.broadcast(f'room:{room_id}', 'user_joined', {

308

'socket_id': socket_id,

309

'room_id': room_id,

310

'timestamp': time.time()

311

})

312

313

return {'status': 'joined', 'room_id': room_id}

314

315

@websocket_rpc

316

def leave_room(self, socket_id, room_id):

317

"""Unsubscribe socket from a chat room"""

318

self.websocket_hub.unsubscribe(socket_id, f'room:{room_id}')

319

320

# Notify other room members

321

self.websocket_hub.broadcast(f'room:{room_id}', 'user_left', {

322

'socket_id': socket_id,

323

'room_id': room_id,

324

'timestamp': time.time()

325

})

326

327

return {'status': 'left', 'room_id': room_id}

328

329

@websocket_rpc

330

def send_message(self, socket_id, room_id, message):

331

"""Send message to all members of a chat room"""

332

try:

333

# Broadcast message to room

334

self.websocket_hub.broadcast(f'room:{room_id}', 'new_message', {

335

'message': message,

336

'sender': socket_id,

337

'room_id': room_id,

338

'timestamp': time.time()

339

})

340

return {'status': 'sent'}

341

except ConnectionNotFound:

342

return {'status': 'error', 'message': 'Socket not found'}

343

344

@websocket_rpc

345

def private_message(self, sender_id, target_id, message):

346

"""Send private message to specific user"""

347

try:

348

# Send direct message to target socket

349

self.websocket_hub.unicast(target_id, 'private_message', {

350

'message': message,

351

'sender': sender_id,

352

'timestamp': time.time()

353

})

354

return {'status': 'sent'}

355

except ConnectionNotFound:

356

return {'status': 'error', 'message': 'Target user not connected'}

357

358

@websocket_rpc

359

def get_my_rooms(self, socket_id):

360

"""Get list of rooms the socket is subscribed to"""

361

subscriptions = self.websocket_hub.get_subscriptions(socket_id)

362

rooms = [sub.replace('room:', '') for sub in subscriptions if sub.startswith('room:')]

363

return {'rooms': rooms}

364

```

365

366

**WebSocket Exceptions:**

367

368

```python { .api }

369

class ConnectionNotFound(NamekoException):

370

"""

371

Raised when attempting to send message to unknown WebSocket connection.

372

373

Parameters:

374

- socket_id: The unknown socket identifier

375

"""

376

```

377

378

### Error Handling

379

380

HTTP-specific error handling and exception mapping.

381

382

```python { .api }

383

from nameko.exceptions import BadRequest, NotFound, Forbidden

384

385

class APIErrorHandler:

386

387

@http('POST', '/api/users', expected_exceptions=(BadRequest, NotFound))

388

def create_user_endpoint(self, request):

389

try:

390

data = request.get_json()

391

if not data.get('email'):

392

raise BadRequest("Email is required")

393

394

user = self.user_service.get_user_by_email(data['email'])

395

if user:

396

raise BadRequest("User already exists")

397

398

return self.user_service.create_user(data)

399

400

except BadRequest as e:

401

return {'error': str(e)}, 400

402

except NotFound as e:

403

return {'error': str(e)}, 404

404

except Exception as e:

405

return {'error': 'Internal server error'}, 500

406

```

407

408

### Middleware and Request Processing

409

410

HTTP services support middleware for request preprocessing, authentication, and response modification.

411

412

```python

413

from nameko.web.handlers import http

414

415

class AuthenticatedAPIService:

416

name = "authenticated_api"

417

418

def _authenticate_request(self, request):

419

"""Middleware-style authentication"""

420

token = request.headers.get('Authorization', '').replace('Bearer ', '')

421

if not token:

422

return None, ('Unauthorized', 401)

423

424

# Validate token logic

425

user = self._validate_token(token)

426

if not user:

427

return None, ('Invalid token', 401)

428

429

return user, None

430

431

@http('GET', '/protected-data')

432

def get_protected_data(self, request):

433

user, error = self._authenticate_request(request)

434

if error:

435

return error

436

437

# Process authenticated request

438

return {'data': 'secret', 'user_id': user['id']}

439

```

440

441

### Configuration

442

443

HTTP service configuration options for server behavior, timeouts, and performance.

444

445

```python

446

# config.yaml

447

WEB_SERVER_ADDRESS: "0.0.0.0:8000"

448

WEB_SERVER_MAX_WORKERS: 10

449

WEB_SERVER_TIMEOUT: 30

450

451

# WebSocket configuration

452

WEBSOCKET_HUB_CONFIG:

453

max_connections: 1000

454

heartbeat_interval: 30

455

```