or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

authentication.mdcore-app.mdexceptions.mdindex.mdmcp.mdopenapi.mdrequest-response.mdstatus-codes.mdtemplating.mdwebsocket.md

openapi.mddocs/

0

# OpenAPI Integration

1

2

Automatic API documentation generation with Swagger UI, route documentation, schema definitions, and custom OpenAPI specification support for comprehensive API documentation.

3

4

## Capabilities

5

6

### OpenAPI Generator

7

8

Main class for managing OpenAPI specification generation and documentation.

9

10

```python { .api }

11

class OpenAPI:

12

def add_openapi_path_obj(

13

self,

14

route_type,

15

endpoint: str,

16

openapi_name: str,

17

openapi_tags: Optional[list],

18

handler

19

):

20

"""

21

Add a route to the OpenAPI specification.

22

23

Args:

24

route_type: HTTP method type

25

endpoint: URL endpoint pattern

26

openapi_name: Name for the operation in OpenAPI docs

27

openapi_tags: List of tags for grouping operations

28

handler: Route handler function

29

"""

30

31

def get_openapi_docs_page(self) -> str:

32

"""

33

Generate Swagger UI HTML page for API documentation.

34

35

Returns:

36

HTML string containing Swagger UI interface

37

"""

38

39

def get_openapi_config(self) -> dict:

40

"""

41

Get the complete OpenAPI specification as a dictionary.

42

43

Returns:

44

OpenAPI specification dictionary following OpenAPI 3.0 standard

45

"""

46

47

def override_openapi(self, openapi_json_spec_path: str):

48

"""

49

Override auto-generated OpenAPI spec with custom specification.

50

51

Args:

52

openapi_json_spec_path: Path to custom OpenAPI JSON specification file

53

"""

54

```

55

56

### Route Documentation

57

58

Use decorators with OpenAPI parameters to document routes automatically.

59

60

```python { .api }

61

# HTTP method decorators support OpenAPI documentation parameters

62

@app.get(

63

endpoint="/users/{user_id}",

64

openapi_name="Get User by ID",

65

openapi_tags=["Users"]

66

)

67

def get_user(request):

68

"""Route handler with OpenAPI documentation"""

69

70

@app.post(

71

endpoint="/users",

72

openapi_name="Create User",

73

openapi_tags=["Users", "Management"]

74

)

75

def create_user(request):

76

"""Route handler with multiple OpenAPI tags"""

77

```

78

79

## Usage Examples

80

81

### Basic OpenAPI Setup

82

83

```python

84

from robyn import Robyn, OpenAPI

85

86

# Create app with OpenAPI support

87

app = Robyn(__file__, openapi=OpenAPI())

88

89

# Document routes with OpenAPI metadata

90

@app.get("/", openapi_name="Get Root", openapi_tags=["General"])

91

def root(request):

92

"""Root endpoint returning welcome message"""

93

return {"message": "Welcome to the API"}

94

95

@app.get("/users", openapi_name="List Users", openapi_tags=["Users"])

96

def list_users(request):

97

"""Get list of all users"""

98

return {"users": ["alice", "bob", "charlie"]}

99

100

@app.get("/users/<user_id>", openapi_name="Get User", openapi_tags=["Users"])

101

def get_user(request):

102

"""Get specific user by ID"""

103

user_id = request.path_params["user_id"]

104

return {"user_id": user_id, "name": f"User {user_id}"}

105

106

@app.post("/users", openapi_name="Create User", openapi_tags=["Users", "Management"])

107

def create_user(request):

108

"""Create a new user"""

109

user_data = request.json()

110

return {"message": "User created", "data": user_data}

111

112

# Serve OpenAPI documentation

113

@app.get("/docs")

114

def docs(request):

115

return app.openapi.get_openapi_docs_page()

116

117

@app.get("/openapi.json")

118

def openapi_spec(request):

119

return app.openapi.get_openapi_config()

120

121

app.start()

122

```

123

124

### Custom OpenAPI Specification

125

126

```python

127

from robyn import Robyn, OpenAPI

128

import json

129

130

app = Robyn(__file__)

131

132

# Create custom OpenAPI specification

133

custom_spec = {

134

"openapi": "3.0.0",

135

"info": {

136

"title": "My Custom API",

137

"description": "A comprehensive API for managing users and resources",

138

"version": "1.0.0",

139

"contact": {

140

"name": "API Support",

141

"url": "https://example.com/support",

142

"email": "support@example.com"

143

},

144

"license": {

145

"name": "MIT",

146

"url": "https://opensource.org/licenses/MIT"

147

}

148

},

149

"servers": [

150

{

151

"url": "https://api.example.com/v1",

152

"description": "Production server"

153

},

154

{

155

"url": "https://staging-api.example.com/v1",

156

"description": "Staging server"

157

}

158

],

159

"paths": {

160

"/users": {

161

"get": {

162

"summary": "List all users",

163

"description": "Retrieve a paginated list of all users in the system",

164

"tags": ["Users"],

165

"parameters": [

166

{

167

"name": "page",

168

"in": "query",

169

"description": "Page number for pagination",

170

"required": False,

171

"schema": {

172

"type": "integer",

173

"minimum": 1,

174

"default": 1

175

}

176

},

177

{

178

"name": "limit",

179

"in": "query",

180

"description": "Number of users per page",

181

"required": False,

182

"schema": {

183

"type": "integer",

184

"minimum": 1,

185

"maximum": 100,

186

"default": 20

187

}

188

}

189

],

190

"responses": {

191

"200": {

192

"description": "List of users",

193

"content": {

194

"application/json": {

195

"schema": {

196

"type": "object",

197

"properties": {

198

"users": {

199

"type": "array",

200

"items": {"$ref": "#/components/schemas/User"}

201

},

202

"pagination": {

203

"$ref": "#/components/schemas/Pagination"

204

}

205

}

206

}

207

}

208

}

209

}

210

}

211

},

212

"post": {

213

"summary": "Create a new user",

214

"description": "Create a new user account with the provided information",

215

"tags": ["Users"],

216

"requestBody": {

217

"required": True,

218

"content": {

219

"application/json": {

220

"schema": {"$ref": "#/components/schemas/CreateUserRequest"}

221

}

222

}

223

},

224

"responses": {

225

"201": {

226

"description": "User created successfully",

227

"content": {

228

"application/json": {

229

"schema": {"$ref": "#/components/schemas/User"}

230

}

231

}

232

},

233

"400": {

234

"description": "Invalid input data",

235

"content": {

236

"application/json": {

237

"schema": {"$ref": "#/components/schemas/Error"}

238

}

239

}

240

}

241

}

242

}

243

}

244

},

245

"components": {

246

"schemas": {

247

"User": {

248

"type": "object",

249

"required": ["id", "username", "email"],

250

"properties": {

251

"id": {

252

"type": "integer",

253

"description": "Unique user identifier"

254

},

255

"username": {

256

"type": "string",

257

"description": "Username for the account",

258

"minLength": 3,

259

"maxLength": 50

260

},

261

"email": {

262

"type": "string",

263

"format": "email",

264

"description": "User's email address"

265

},

266

"full_name": {

267

"type": "string",

268

"description": "Full name of the user"

269

},

270

"created_at": {

271

"type": "string",

272

"format": "date-time",

273

"description": "Account creation timestamp"

274

}

275

}

276

},

277

"CreateUserRequest": {

278

"type": "object",

279

"required": ["username", "email", "password"],

280

"properties": {

281

"username": {

282

"type": "string",

283

"minLength": 3,

284

"maxLength": 50

285

},

286

"email": {

287

"type": "string",

288

"format": "email"

289

},

290

"password": {

291

"type": "string",

292

"minLength": 8,

293

"description": "User password (will be hashed)"

294

},

295

"full_name": {

296

"type": "string"

297

}

298

}

299

},

300

"Pagination": {

301

"type": "object",

302

"properties": {

303

"page": {

304

"type": "integer",

305

"description": "Current page number"

306

},

307

"limit": {

308

"type": "integer",

309

"description": "Items per page"

310

},

311

"total": {

312

"type": "integer",

313

"description": "Total number of items"

314

},

315

"has_next": {

316

"type": "boolean",

317

"description": "Whether there are more pages"

318

}

319

}

320

},

321

"Error": {

322

"type": "object",

323

"required": ["message"],

324

"properties": {

325

"message": {

326

"type": "string",

327

"description": "Error message"

328

},

329

"code": {

330

"type": "string",

331

"description": "Error code"

332

},

333

"details": {

334

"type": "object",

335

"description": "Additional error details"

336

}

337

}

338

}

339

},

340

"securitySchemes": {

341

"BearerAuth": {

342

"type": "http",

343

"scheme": "bearer",

344

"bearerFormat": "JWT"

345

},

346

"ApiKeyAuth": {

347

"type": "apiKey",

348

"in": "header",

349

"name": "X-API-Key"

350

}

351

}

352

},

353

"security": [

354

{"BearerAuth": []},

355

{"ApiKeyAuth": []}

356

]

357

}

358

359

# Save custom spec to file

360

with open("custom_openapi.json", "w") as f:

361

json.dump(custom_spec, f, indent=2)

362

363

# Use custom specification

364

openapi = OpenAPI()

365

openapi.override_openapi("custom_openapi.json")

366

app = Robyn(__file__, openapi=openapi)

367

368

# Implement the actual routes

369

@app.get("/users")

370

def list_users(request):

371

page = int(request.query_params.get("page", "1"))

372

limit = int(request.query_params.get("limit", "20"))

373

374

# Simulate user data

375

users = [

376

{"id": i, "username": f"user{i}", "email": f"user{i}@example.com"}

377

for i in range((page-1)*limit + 1, page*limit + 1)

378

]

379

380

return {

381

"users": users,

382

"pagination": {

383

"page": page,

384

"limit": limit,

385

"total": 1000,

386

"has_next": page * limit < 1000

387

}

388

}

389

390

@app.post("/users")

391

def create_user(request):

392

user_data = request.json()

393

394

# Validate required fields

395

if not all(field in user_data for field in ["username", "email", "password"]):

396

return Response(400, {}, {

397

"message": "Missing required fields",

398

"code": "MISSING_FIELDS",

399

"details": {"required": ["username", "email", "password"]}

400

})

401

402

# Simulate user creation

403

new_user = {

404

"id": 1001,

405

"username": user_data["username"],

406

"email": user_data["email"],

407

"full_name": user_data.get("full_name"),

408

"created_at": "2023-12-01T12:00:00Z"

409

}

410

411

return Response(201, {}, new_user)

412

413

# Documentation endpoints

414

@app.get("/docs")

415

def swagger_ui(request):

416

return app.openapi.get_openapi_docs_page()

417

418

@app.get("/openapi.json")

419

def openapi_json(request):

420

return app.openapi.get_openapi_config()

421

422

app.start()

423

```

424

425

### Advanced OpenAPI with Authentication

426

427

```python

428

from robyn import Robyn, OpenAPI, AuthenticationHandler, BearerGetter

429

430

class SimpleAuthHandler(AuthenticationHandler):

431

def __init__(self):

432

super().__init__(BearerGetter())

433

434

def authenticate(self, request):

435

token = self.token_getter.get_token(request)

436

if token == "valid-token":

437

return Identity(claims={"user_id": "123", "role": "admin"})

438

return None

439

440

app = Robyn(__file__)

441

442

# Configure authentication

443

auth_handler = SimpleAuthHandler()

444

app.configure_authentication(auth_handler)

445

446

# Public endpoints

447

@app.get("/", openapi_name="API Info", openapi_tags=["General"])

448

def api_info(request):

449

"""Get API information and status"""

450

return {

451

"name": "My Secure API",

452

"version": "1.0.0",

453

"status": "healthy"

454

}

455

456

@app.post("/auth/login", openapi_name="Login", openapi_tags=["Authentication"])

457

def login(request):

458

"""Authenticate user and get access token"""

459

credentials = request.json()

460

# Simplified login logic

461

if credentials.get("username") == "admin":

462

return {"token": "valid-token", "expires_in": 3600}

463

return Response(401, {}, {"error": "Invalid credentials"})

464

465

# Protected endpoints

466

@app.get("/profile", auth_required=True, openapi_name="Get Profile", openapi_tags=["User"])

467

def get_profile(request):

468

"""Get current user's profile (requires authentication)"""

469

return {

470

"user_id": request.identity.claims["user_id"],

471

"role": request.identity.claims["role"]

472

}

473

474

@app.get("/admin/stats", auth_required=True, openapi_name="Get Admin Stats", openapi_tags=["Admin"])

475

def admin_stats(request):

476

"""Get administrative statistics (requires authentication)"""

477

if request.identity.claims.get("role") != "admin":

478

return Response(403, {}, {"error": "Admin access required"})

479

480

return {

481

"total_users": 150,

482

"active_sessions": 23,

483

"server_uptime": "5 days"

484

}

485

486

# Custom documentation with authentication info

487

@app.get("/docs")

488

def docs_with_auth(request):

489

"""Swagger UI with authentication support"""

490

html = app.openapi.get_openapi_docs_page()

491

492

# You could customize the HTML here to add authentication instructions

493

auth_info = """

494

<div style="background: #f8f9fa; padding: 15px; margin: 10px 0; border-radius: 5px;">

495

<h4>Authentication</h4>

496

<p>This API requires authentication for protected endpoints.</p>

497

<p>To authenticate:</p>

498

<ol>

499

<li>Call POST /auth/login with {"username": "admin"}</li>

500

<li>Copy the returned token</li>

501

<li>Click "Authorize" button and enter: Bearer YOUR_TOKEN</li>

502

</ol>

503

</div>

504

"""

505

506

# Insert auth info after the body tag

507

html = html.replace("<body>", f"<body>{auth_info}")

508

509

return html

510

511

app.start()

512

```

513

514

### OpenAPI with Multiple Routers

515

516

```python

517

from robyn import Robyn, SubRouter, OpenAPI

518

519

# Create app with OpenAPI

520

app = Robyn(__file__, openapi=OpenAPI())

521

522

# Create sub-routers with different purposes

523

api_v1 = SubRouter(__file__, prefix="/api/v1", openapi=OpenAPI())

524

admin_router = SubRouter(__file__, prefix="/admin", openapi=OpenAPI())

525

526

# API v1 routes

527

@api_v1.get("/users", openapi_name="List Users V1", openapi_tags=["Users", "V1"])

528

def list_users_v1(request):

529

"""List users - API Version 1"""

530

return {"users": [], "version": "v1"}

531

532

@api_v1.post("/users", openapi_name="Create User V1", openapi_tags=["Users", "V1"])

533

def create_user_v1(request):

534

"""Create user - API Version 1"""

535

return {"message": "User created", "version": "v1"}

536

537

# Admin routes

538

@admin_router.get("/dashboard", openapi_name="Admin Dashboard", openapi_tags=["Admin"])

539

def admin_dashboard(request):

540

"""Administrative dashboard overview"""

541

return {"dashboard": "admin", "stats": {"users": 100}}

542

543

@admin_router.get("/logs", openapi_name="View Logs", openapi_tags=["Admin", "Logs"])

544

def view_logs(request):

545

"""View system logs"""

546

return {"logs": ["Log entry 1", "Log entry 2"]}

547

548

# Include routers

549

app.include_router(api_v1)

550

app.include_router(admin_router)

551

552

# Main app routes

553

@app.get("/", openapi_name="Root", openapi_tags=["General"])

554

def root(request):

555

"""Root endpoint with API information"""

556

return {

557

"api": "My Multi-Router API",

558

"version": "1.0.0",

559

"endpoints": {

560

"docs": "/docs",

561

"openapi": "/openapi.json",

562

"api_v1": "/api/v1",

563

"admin": "/admin"

564

}

565

}

566

567

# Consolidated documentation

568

@app.get("/docs")

569

def comprehensive_docs(request):

570

"""Comprehensive API documentation"""

571

return app.openapi.get_openapi_docs_page()

572

573

@app.get("/openapi.json")

574

def full_openapi_spec(request):

575

"""Complete OpenAPI specification"""

576

return app.openapi.get_openapi_config()

577

578

app.start()

579

```