or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

analysis-metrics.mdapi-definition-reduction.mdextensions-customization.mdindex.mdopenapi-definition-management.mdoperation-discovery-analysis.mdparameter-handling-json-schema.mdrequest-response-management.mdschema-dereferencing-references.mdsecurity-authentication.mdserver-url-management.mdutils.md

schema-dereferencing-references.mddocs/

0

# Schema Dereferencing and References

1

2

Advanced schema dereferencing with circular reference detection and resolution for OpenAPI definitions.

3

4

## Capabilities

5

6

### Dereference API Definition

7

8

Resolve all `$ref` pointers in the OpenAPI definition to eliminate references and circular dependencies.

9

10

```typescript { .api }

11

/**

12

* Dereference the OpenAPI definition to resolve all $ref pointers

13

* @param opts - Dereferencing options

14

* @returns Promise resolving when dereferencing is complete

15

*/

16

dereference(opts?: {

17

/** Callback function called when dereferencing completes (for debugging) */

18

cb?: () => void;

19

/** Preserve component schema names as JSON Schema titles */

20

preserveRefAsJSONSchemaTitle?: boolean;

21

}): Promise<any>;

22

```

23

24

**Usage Examples:**

25

26

```typescript

27

import Oas from "oas";

28

29

const oas = new Oas(definitionWithRefs);

30

31

// Basic dereferencing

32

await oas.dereference();

33

34

// With title preservation for code generation

35

await oas.dereference({

36

preserveRefAsJSONSchemaTitle: true

37

});

38

39

// With completion callback

40

await oas.dereference({

41

cb: () => console.log("Dereferencing complete!")

42

});

43

44

// After dereferencing, all $ref pointers are resolved

45

const dereferencedDef = oas.getDefinition();

46

// No more { $ref: "#/components/schemas/User" } - actual schema objects instead

47

```

48

49

### Get Circular References

50

51

Retrieve circular reference pointers detected during dereferencing.

52

53

```typescript { .api }

54

/**

55

* Get circular reference pointers found during dereferencing

56

* @returns Array of JSON pointer strings that form circular references

57

* @throws Error if dereference() hasn't been called first

58

*/

59

getCircularReferences(): string[];

60

```

61

62

**Usage Examples:**

63

64

```typescript

65

// Must dereference first

66

await oas.dereference();

67

68

// Get circular references

69

const circularRefs = oas.getCircularReferences();

70

71

if (circularRefs.length > 0) {

72

console.log("Found circular references:");

73

circularRefs.forEach(ref => {

74

console.log(` ${ref}`);

75

});

76

77

// Handle circular references in your application

78

console.log("These schemas reference themselves or form reference cycles");

79

} else {

80

console.log("No circular references found");

81

}

82

83

// Example circular reference:

84

// User schema -> Profile schema -> User schema

85

// Results in: ["#/components/schemas/User", "#/components/schemas/Profile"]

86

```

87

88

## Advanced Dereferencing Features

89

90

### Preserving Reference Names

91

92

When generating code or documentation, preserve original schema names:

93

94

```typescript

95

await oas.dereference({

96

preserveRefAsJSONSchemaTitle: true

97

});

98

99

const definition = oas.getDefinition();

100

101

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

102

// Becomes: { title: "User", type: "object", properties: {...} }

103

104

// Useful for code generators that need original type names

105

const schemas = definition.components?.schemas;

106

Object.entries(schemas || {}).forEach(([name, schema]) => {

107

if (schema.title) {

108

console.log(`Schema ${name} preserved as title: ${schema.title}`);

109

}

110

});

111

```

112

113

### Multi-Promise Handling

114

115

The dereferencing system handles multiple concurrent calls efficiently:

116

117

```typescript

118

// Multiple calls to dereference() return the same promise

119

const promise1 = oas.dereference();

120

const promise2 = oas.dereference(); // Returns same promise as promise1

121

const promise3 = oas.dereference(); // Returns same promise as promise1

122

123

// All resolve when dereferencing completes

124

await Promise.all([promise1, promise2, promise3]);

125

126

console.log("All dereferencing calls completed");

127

```

128

129

### Reference Metadata Preservation

130

131

The library preserves useful metadata during dereferencing:

132

133

```typescript

134

await oas.dereference();

135

136

const definition = oas.getDefinition();

137

138

// Check for preserved reference names

139

function findPreservedRefs(obj: any, path = ""): void {

140

if (obj && typeof obj === 'object') {

141

if (obj['x-readme-ref-name']) {

142

console.log(`${path}: originally ${obj['x-readme-ref-name']}`);

143

}

144

145

Object.entries(obj).forEach(([key, value]) => {

146

findPreservedRefs(value, path ? `${path}.${key}` : key);

147

});

148

}

149

}

150

151

findPreservedRefs(definition);

152

```

153

154

## Circular Reference Handling

155

156

### Detection and Management

157

158

```typescript

159

await oas.dereference();

160

const circularRefs = oas.getCircularReferences();

161

162

// Analyze circular reference patterns

163

const refCounts = circularRefs.reduce((acc, ref) => {

164

acc[ref] = (acc[ref] || 0) + 1;

165

return acc;

166

}, {} as Record<string, number>);

167

168

console.log("Circular reference frequency:", refCounts);

169

170

// Most commonly referenced schemas in cycles

171

const mostCircular = Object.entries(refCounts)

172

.sort(([,a], [,b]) => b - a)

173

.slice(0, 5);

174

175

console.log("Most circular schemas:", mostCircular);

176

```

177

178

### Circular Reference Patterns

179

180

Common patterns that create circular references:

181

182

```yaml

183

# Self-referencing schema

184

components:

185

schemas:

186

Node:

187

type: object

188

properties:

189

children:

190

type: array

191

items:

192

$ref: '#/components/schemas/Node' # Circular!

193

194

# Mutual reference cycle

195

components:

196

schemas:

197

User:

198

type: object

199

properties:

200

profile:

201

$ref: '#/components/schemas/Profile'

202

Profile:

203

type: object

204

properties:

205

user:

206

$ref: '#/components/schemas/User' # Circular!

207

```

208

209

### Working with Circular Schemas

210

211

```typescript

212

// After dereferencing, circular refs remain as $ref pointers

213

await oas.dereference();

214

const definition = oas.getDefinition();

215

216

// Find remaining $ref pointers (these are circular)

217

function findRemainingRefs(obj: any): string[] {

218

const refs: string[] = [];

219

220

if (obj && typeof obj === 'object') {

221

if (obj.$ref && typeof obj.$ref === 'string') {

222

refs.push(obj.$ref);

223

}

224

225

Object.values(obj).forEach(value => {

226

refs.push(...findRemainingRefs(value));

227

});

228

}

229

230

return refs;

231

}

232

233

const remainingRefs = findRemainingRefs(definition);

234

console.log("Remaining $ref pointers (circular):", remainingRefs);

235

```

236

237

## Error Handling and Edge Cases

238

239

### Dereferencing Errors

240

241

```typescript

242

try {

243

await oas.dereference();

244

} catch (error) {

245

console.error("Dereferencing failed:", error.message);

246

247

// Common issues:

248

// - Broken $ref pointers

249

// - Invalid JSON Schema

250

// - Network issues (for external refs - though disabled by default)

251

}

252

```

253

254

### Invalid Reference Handling

255

256

```typescript

257

// Broken references are handled gracefully

258

const definitionWithBrokenRefs = {

259

openapi: "3.1.0",

260

info: { title: "API", version: "1.0.0" },

261

paths: {

262

"/test": {

263

get: {

264

responses: {

265

"200": {

266

description: "Success",

267

content: {

268

"application/json": {

269

schema: { $ref: "#/components/schemas/NonExistent" } // Broken ref

270

}

271

}

272

}

273

}

274

}

275

}

276

}

277

};

278

279

const oas = new Oas(definitionWithBrokenRefs);

280

await oas.dereference(); // Doesn't throw, handles gracefully

281

282

// Broken refs remain as $ref pointers

283

const circularRefs = oas.getCircularReferences(); // []

284

```

285

286

### Memory Management

287

288

For large APIs with many references:

289

290

```typescript

291

// Dereferencing can increase memory usage significantly

292

const beforeSize = JSON.stringify(oas.getDefinition()).length;

293

294

await oas.dereference();

295

296

const afterSize = JSON.stringify(oas.getDefinition()).length;

297

const expansion = ((afterSize - beforeSize) / beforeSize * 100).toFixed(1);

298

299

console.log(`Definition expanded by ${expansion}% after dereferencing`);

300

console.log(`Before: ${beforeSize} chars, After: ${afterSize} chars`);

301

```

302

303

## Integration Patterns

304

305

### Code Generation Pipeline

306

307

```typescript

308

// Prepare definition for code generation

309

await oas.dereference({

310

preserveRefAsJSONSchemaTitle: true

311

});

312

313

// Now all schemas are fully resolved with preserved names

314

const schemas = oas.getDefinition().components?.schemas || {};

315

316

Object.entries(schemas).forEach(([name, schema]) => {

317

generateTypeDefinition(name, schema);

318

});

319

320

function generateTypeDefinition(name: string, schema: any) {

321

// Use schema.title for the original reference name if available

322

const typeName = schema.title || name;

323

console.log(`Generating type: ${typeName}`);

324

325

// Schema is fully dereferenced - no $ref pointers to resolve

326

if (schema.properties) {

327

Object.entries(schema.properties).forEach(([prop, propSchema]) => {

328

console.log(` ${prop}: ${propSchema.type || 'unknown'}`);

329

});

330

}

331

}

332

```

333

334

### Documentation Generation

335

336

```typescript

337

// Generate docs with circular reference warnings

338

await oas.dereference();

339

const circularRefs = oas.getCircularReferences();

340

341

// Warn about circular references in documentation

342

if (circularRefs.length > 0) {

343

console.log("⚠️ Circular References Detected:");

344

circularRefs.forEach(ref => {

345

const schemaName = ref.split('/').pop();

346

console.log(` ${schemaName} contains circular references`);

347

});

348

}

349

```

350

351

### Validation Pipeline

352

353

```typescript

354

// Ensure definition is fully dereferenced before validation

355

if (!oas.dereferencing?.complete) {

356

await oas.dereference();

357

}

358

359

// Now safe to validate without worrying about $ref resolution

360

const definition = oas.getDefinition();

361

validateOpenAPIDefinition(definition);

362

```