or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

decorators.mdfastify-adapter.mdindex.mdinterfaces.md

interfaces.mddocs/

0

# Interfaces and Configuration

1

2

Enhanced interfaces for NestJS applications with Fastify-specific functionality and configuration options for static assets, view engines, and body parsing.

3

4

## Capabilities

5

6

### NestFastifyApplication Interface

7

8

Enhanced NestJS application interface that provides Fastify-specific methods and functionality.

9

10

```typescript { .api }

11

/**

12

* Enhanced NestJS application interface with Fastify-specific methods

13

* Extends the standard INestApplication with Fastify adapter capabilities

14

*/

15

interface NestFastifyApplication<TServer extends RawServerBase = RawServerDefault>

16

extends INestApplication<TServer> {

17

18

/**

19

* Returns the underlying HTTP adapter bound to the Fastify app

20

*/

21

getHttpAdapter(): HttpServer<FastifyRequest, FastifyReply, FastifyInstance>;

22

23

/**

24

* Registers a Fastify plugin with the application

25

* Wrapper around the native fastify.register() method

26

* @param plugin - Fastify plugin (callback, async, or promise-wrapped)

27

* @param opts - Plugin registration options

28

*/

29

register<Options extends FastifyPluginOptions = any>(

30

plugin:

31

| FastifyPluginCallback<Options>

32

| FastifyPluginAsync<Options>

33

| Promise<{ default: FastifyPluginCallback<Options> }>

34

| Promise<{ default: FastifyPluginAsync<Options> }>,

35

opts?: FastifyRegisterOptions<Options>

36

): Promise<FastifyInstance>;

37

38

/**

39

* Registers custom body parsers on the fly

40

* Respects the application's rawBody option

41

* @param type - Content type(s) to parse

42

* @param options - Body parser options

43

* @param parser - Custom parser function

44

*/

45

useBodyParser<TServer extends RawServerBase = RawServerBase>(

46

type: string | string[] | RegExp,

47

options?: NestFastifyBodyParserOptions,

48

parser?: FastifyBodyParser<Buffer, TServer>

49

): this;

50

51

/**

52

* Configures static asset serving

53

* @param options - Static asset configuration options

54

*/

55

useStaticAssets(options: FastifyStaticOptions): this;

56

57

/**

58

* Enables Cross-Origin Resource Sharing (CORS)

59

* @param options - CORS configuration options

60

*/

61

enableCors(options?: FastifyCorsOptions): void;

62

63

/**

64

* Sets up a view engine for template rendering

65

* @param options - View engine configuration (object only, string will cause error)

66

*/

67

setViewEngine(options: FastifyViewOptions | string): this;

68

69

/**

70

* Injects a test request for testing purposes

71

* Returns a chain for building the request

72

*/

73

inject(): LightMyRequestChain;

74

75

/**

76

* Injects a test request with specific options

77

* @param opts - Request options or URL string

78

*/

79

inject(opts: InjectOptions | string): Promise<LightMyRequestResponse>;

80

81

/**

82

* Starts the application server

83

* Multiple overloads for different parameter combinations

84

*/

85

listen(

86

opts: FastifyListenOptions,

87

callback?: (err: Error | null, address: string) => void

88

): Promise<TServer>;

89

listen(opts?: FastifyListenOptions): Promise<TServer>;

90

listen(callback?: (err: Error | null, address: string) => void): Promise<TServer>;

91

listen(

92

port: number | string,

93

callback?: (err: Error | null, address: string) => void

94

): Promise<TServer>;

95

listen(

96

port: number | string,

97

address: string,

98

callback?: (err: Error | null, address: string) => void

99

): Promise<TServer>;

100

listen(

101

port: number | string,

102

address: string,

103

backlog: number,

104

callback?: (err: Error | null, address: string) => void

105

): Promise<TServer>;

106

}

107

```

108

109

**Usage Examples:**

110

111

```typescript

112

import { NestFactory } from '@nestjs/core';

113

import { FastifyAdapter, NestFastifyApplication } from '@nestjs/platform-fastify';

114

import { AppModule } from './app.module';

115

import * as path from 'path';

116

117

async function bootstrap() {

118

// Create application with proper typing

119

const app = await NestFactory.create<NestFastifyApplication>(

120

AppModule,

121

new FastifyAdapter()

122

);

123

124

// Register Fastify plugins

125

await app.register(require('@fastify/helmet'));

126

await app.register(require('@fastify/rate-limit'), {

127

max: 100,

128

timeWindow: '1 minute'

129

});

130

131

// Configure body parsing

132

app.useBodyParser('application/xml', { bodyLimit: 1024 * 1024 });

133

134

// Setup static assets

135

app.useStaticAssets({

136

root: path.join(__dirname, '..', 'public'),

137

prefix: '/static/',

138

});

139

140

// Enable CORS

141

app.enableCors({

142

origin: ['http://localhost:3000', 'https://example.com'],

143

credentials: true

144

});

145

146

// Setup view engine

147

app.setViewEngine({

148

engine: {

149

handlebars: require('handlebars')

150

},

151

templates: path.join(__dirname, '..', 'views'),

152

options: {

153

partials: {

154

header: 'partials/header.hbs',

155

footer: 'partials/footer.hbs'

156

}

157

}

158

});

159

160

// Start server

161

await app.listen({

162

port: 3000,

163

host: '0.0.0.0'

164

});

165

}

166

```

167

168

### Body Parser Configuration

169

170

Type definition for configuring custom body parsers.

171

172

```typescript { .api }

173

/**

174

* Options for configuring Fastify body parsers

175

* Excludes 'parseAs' property which is handled internally

176

*/

177

type NestFastifyBodyParserOptions = Omit<

178

Parameters<AddContentTypeParser>[1],

179

'parseAs'

180

>;

181

```

182

183

Common body parser options include:

184

185

```typescript

186

interface BodyParserOptions {

187

/** Maximum body size in bytes */

188

bodyLimit?: number;

189

/** Parser-specific configuration */

190

parseAs?: 'string' | 'buffer';

191

/** Content type parser configuration */

192

contentTypeParser?: {

193

// Parser implementation details

194

};

195

}

196

```

197

198

**Usage Examples:**

199

200

```typescript

201

// Register XML body parser

202

app.useBodyParser('application/xml', {

203

bodyLimit: 2 * 1024 * 1024, // 2MB limit

204

}, (req, body, done) => {

205

// Custom XML parsing logic

206

const xmlParser = require('fast-xml-parser');

207

try {

208

const parsed = xmlParser.parse(body.toString());

209

done(null, parsed);

210

} catch (error) {

211

done(error);

212

}

213

});

214

215

// Register multiple content types

216

app.useBodyParser(['text/plain', 'text/html'], {

217

bodyLimit: 1024 * 1024, // 1MB limit

218

}, (req, body, done) => {

219

done(null, body.toString());

220

});

221

```

222

223

### Static Assets Configuration

224

225

Configuration interface for serving static files through Fastify.

226

227

```typescript { .api }

228

/**

229

* Configuration options for static asset serving

230

* Based on @fastify/static plugin options

231

*/

232

interface FastifyStaticOptions extends SendOptions {

233

/** Root directory or directories for static files */

234

root: string | string[] | URL | URL[];

235

236

/** URL prefix for serving static files */

237

prefix?: string;

238

239

/** Avoid trailing slash in prefix */

240

prefixAvoidTrailingSlash?: boolean;

241

242

/** Whether to serve files (can be disabled for decorateReply only) */

243

serve?: boolean;

244

245

/** Add reply.sendFile method to reply object */

246

decorateReply?: boolean;

247

248

/** Hide from OpenAPI/JSON schema */

249

schemaHide?: boolean;

250

251

/** Function to set custom headers */

252

setHeaders?: (res: SetHeadersResponse, path: string, stat: Stats) => void;

253

254

/** Enable redirect from folder to folder/ */

255

redirect?: boolean;

256

257

/** Enable wildcard matching */

258

wildcard?: boolean;

259

260

/** Enable/configure directory listing */

261

list?: boolean | ListOptionsJsonFormat | ListOptionsHtmlFormat;

262

263

/** Function to filter allowed paths */

264

allowedPath?: (

265

pathName: string,

266

root: string,

267

request: FastifyRequest

268

) => boolean;

269

270

/** Look for pre-compressed files (.gz, .br) */

271

preCompressed?: boolean;

272

273

/** Route constraints for static serving */

274

constraints?: RouteOptions['constraints'];

275

276

// Inherited from SendOptions

277

acceptRanges?: boolean;

278

cacheControl?: boolean;

279

dotfiles?: 'allow' | 'deny' | 'ignore';

280

etag?: boolean;

281

extensions?: string[];

282

immutable?: boolean;

283

index?: string[] | string | false;

284

lastModified?: boolean;

285

maxAge?: string | number;

286

}

287

288

interface SetHeadersResponse {

289

getHeader: FastifyReply['getHeader'];

290

setHeader: FastifyReply['header'];

291

readonly filename: string;

292

statusCode: number;

293

}

294

295

interface ListOptionsJsonFormat {

296

format: 'json';

297

names: string[];

298

extendedFolderInfo?: boolean;

299

jsonFormat?: 'names' | 'extended';

300

render?: ListRender;

301

}

302

303

interface ListOptionsHtmlFormat {

304

format: 'html';

305

names: string[];

306

extendedFolderInfo?: boolean;

307

jsonFormat?: 'names' | 'extended';

308

render: ListRender;

309

}

310

311

interface ListRender {

312

(dirs: ListDir[], files: ListFile[]): string;

313

}

314

```

315

316

**Usage Examples:**

317

318

```typescript

319

// Basic static assets

320

app.useStaticAssets({

321

root: path.join(__dirname, '..', 'public'),

322

prefix: '/static/',

323

});

324

325

// Advanced configuration

326

app.useStaticAssets({

327

root: [

328

path.join(__dirname, '..', 'public'),

329

path.join(__dirname, '..', 'uploads')

330

],

331

prefix: '/assets/',

332

maxAge: '1d',

333

etag: true,

334

lastModified: true,

335

immutable: true,

336

preCompressed: true,

337

setHeaders: (res, path, stat) => {

338

if (path.endsWith('.pdf')) {

339

res.setHeader('Content-Type', 'application/pdf');

340

res.setHeader('Content-Security-Policy', "default-src 'self'");

341

}

342

},

343

allowedPath: (pathName, root, request) => {

344

// Only allow access to certain files based on user permissions

345

return !pathName.includes('private') || request.headers.authorization;

346

},

347

list: {

348

format: 'json',

349

names: ['index.html'],

350

extendedFolderInfo: true

351

}

352

});

353

354

// Serving with constraints

355

app.useStaticAssets({

356

root: path.join(__dirname, '..', 'admin-assets'),

357

prefix: '/admin/',

358

constraints: {

359

host: 'admin.example.com'

360

}

361

});

362

```

363

364

### View Engine Configuration

365

366

Configuration interface for template engines and view rendering.

367

368

```typescript { .api }

369

/**

370

* Configuration options for view engine setup

371

* Based on @fastify/view plugin options

372

*/

373

interface FastifyViewOptions {

374

/** Template engine configuration */

375

engine: {

376

/** EJS template engine */

377

ejs?: any;

378

/** Eta template engine */

379

eta?: any;

380

/** Nunjucks template engine */

381

nunjucks?: any;

382

/** Pug template engine */

383

pug?: any;

384

/** Handlebars template engine */

385

handlebars?: any;

386

/** Mustache template engine */

387

mustache?: any;

388

/** Art Template engine */

389

'art-template'?: any;

390

/** Twig template engine */

391

twig?: any;

392

/** Liquid template engine */

393

liquid?: any;

394

/** doT template engine */

395

dot?: any;

396

};

397

398

/** Template directory path(s) */

399

templates?: string | string[];

400

401

/** Include file extension in template names */

402

includeViewExtension?: boolean;

403

404

/** Engine-specific options */

405

options?: object;

406

407

/** Character encoding for templates */

408

charset?: string;

409

410

/** Maximum number of cached templates */

411

maxCache?: number;

412

413

/** Production mode optimizations */

414

production?: boolean;

415

416

/** Default context for all templates */

417

defaultContext?: object;

418

419

/** Default layout template */

420

layout?: string;

421

422

/** Root directory for templates */

423

root?: string;

424

425

/** Default view file extension */

426

viewExt?: string;

427

428

/** Property name for reply.view method */

429

propertyName?: string;

430

431

/** Property name for async reply method */

432

asyncProperyName?: string;

433

}

434

```

435

436

**Usage Examples:**

437

438

```typescript

439

// Handlebars setup

440

app.setViewEngine({

441

engine: {

442

handlebars: require('handlebars')

443

},

444

templates: path.join(__dirname, '..', 'views'),

445

options: {

446

partials: {

447

header: 'partials/header.hbs',

448

footer: 'partials/footer.hbs',

449

sidebar: 'partials/sidebar.hbs'

450

},

451

helpers: {

452

formatDate: (date) => new Date(date).toLocaleDateString(),

453

uppercase: (str) => str.toUpperCase()

454

}

455

},

456

layout: 'layouts/main.hbs',

457

viewExt: 'hbs',

458

defaultContext: {

459

siteName: 'My Application',

460

year: new Date().getFullYear()

461

}

462

});

463

464

// Pug setup with production optimizations

465

app.setViewEngine({

466

engine: {

467

pug: require('pug')

468

},

469

templates: path.join(__dirname, '..', 'views'),

470

production: process.env.NODE_ENV === 'production',

471

maxCache: 100,

472

options: {

473

pretty: process.env.NODE_ENV !== 'production',

474

cache: process.env.NODE_ENV === 'production',

475

basedir: path.join(__dirname, '..', 'views')

476

}

477

});

478

479

// Multiple template directories

480

app.setViewEngine({

481

engine: {

482

ejs: require('ejs')

483

},

484

templates: [

485

path.join(__dirname, '..', 'views'),

486

path.join(__dirname, '..', 'shared-templates')

487

],

488

options: {

489

delimiter: '?',

490

openDelimiter: '<',

491

closeDelimiter: '>',

492

cache: true

493

}

494

});

495

```

496

497

### Testing Interfaces

498

499

Interfaces for testing with light-my-request integration.

500

501

```typescript { .api }

502

/**

503

* Options for injecting test requests

504

*/

505

interface InjectOptions {

506

method?: string;

507

url?: string;

508

path?: string;

509

query?: string | Record<string, any>;

510

payload?: any;

511

headers?: Record<string, string>;

512

cookies?: Record<string, string>;

513

remoteAddress?: string;

514

server?: any;

515

simulate?: {

516

end?: boolean;

517

split?: boolean;

518

error?: boolean;

519

close?: boolean;

520

};

521

validate?: boolean;

522

authority?: string;

523

}

524

525

/**

526

* Chain interface for building test requests

527

*/

528

interface LightMyRequestChain {

529

get(url: string): LightMyRequestChain;

530

post(url: string): LightMyRequestChain;

531

put(url: string): LightMyRequestChain;

532

patch(url: string): LightMyRequestChain;

533

delete(url: string): LightMyRequestChain;

534

head(url: string): LightMyRequestChain;

535

options(url: string): LightMyRequestChain;

536

headers(headers: Record<string, string>): LightMyRequestChain;

537

payload(payload: any): LightMyRequestChain;

538

query(query: Record<string, any>): LightMyRequestChain;

539

cookies(cookies: Record<string, string>): LightMyRequestChain;

540

end(): Promise<LightMyRequestResponse>;

541

}

542

543

/**

544

* Response from injected test requests

545

*/

546

interface LightMyRequestResponse {

547

statusCode: number;

548

statusMessage: string;

549

headers: Record<string, string>;

550

rawPayload: Buffer;

551

payload: string;

552

body: string;

553

json(): any;

554

cookies: Array<{

555

name: string;

556

value: string;

557

path?: string;

558

domain?: string;

559

expires?: Date;

560

httpOnly?: boolean;

561

secure?: boolean;

562

sameSite?: string;

563

}>;

564

}

565

```

566

567

**Usage Examples:**

568

569

```typescript

570

// In test files

571

describe('ProductsController', () => {

572

let app: NestFastifyApplication;

573

574

beforeEach(async () => {

575

const module = await Test.createTestingModule({

576

controllers: [ProductsController],

577

}).compile();

578

579

app = module.createNestApplication<NestFastifyApplication>(

580

new FastifyAdapter()

581

);

582

await app.init();

583

});

584

585

it('should return products', async () => {

586

const response = await app.inject({

587

method: 'GET',

588

url: '/products',

589

query: { page: '1', limit: '10' }

590

});

591

592

expect(response.statusCode).toBe(200);

593

expect(response.json()).toEqual({

594

products: expect.any(Array),

595

total: expect.any(Number)

596

});

597

});

598

599

it('should create product', async () => {

600

const productData = {

601

name: 'Test Product',

602

price: 29.99,

603

category: 'electronics'

604

};

605

606

const response = await app.inject()

607

.post('/products')

608

.headers({ 'content-type': 'application/json' })

609

.payload(productData)

610

.end();

611

612

expect(response.statusCode).toBe(201);

613

expect(response.json()).toMatchObject(productData);

614

});

615

});

616

```