> ## Documentation Index
> Fetch the complete documentation index at: https://developers.fd.xyz/llms.txt
> Use this file to discover all available pages before exploring further.

# NestJS Integration

> Protect NestJS APIs with blockchain micropayments using decorators

## Overview

The **@1stdigital/prism-nestjs** package provides a full-featured NestJS module with decorators, guards, and interceptors for payment-protecting your API routes.

<CardGroup cols={3}>
  <Card title="Decorator-Based" icon="at">
    @Payment() and @Payer() decorators
  </Card>

  <Card title="Module System" icon="boxes-stacked">
    PrismModule.forRoot() configuration
  </Card>

  <Card title="Dependency Injection" icon="puzzle-piece">
    Works with NestJS DI system
  </Card>
</CardGroup>

***

## Installation

<Tabs>
  <Tab title="npm">
    ```bash theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
    npm install @1stdigital/prism-nestjs @nestjs/core @nestjs/common
    ```
  </Tab>

  {" "}

  <Tab title="yarn">
    `bash yarn add @1stdigital/prism-nestjs @nestjs/core @nestjs/common `
  </Tab>

  <Tab title="pnpm">
    ```bash theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
    pnpm add @1stdigital/prism-nestjs @nestjs/core @nestjs/common
    ```
  </Tab>
</Tabs>

***

<Note>
  The canonical SDK config is `identifyToken` (`PRISM_IDENTIFY_TOKEN` env var) — formerly `apiKey` / `PRISM_API_KEY`. SDKs accept the legacy names as fallback during the migration window; new code should use the canonical names.
</Note>

## Quick Start

```typescript theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
// app.module.ts
import { Module } from "@nestjs/common";
import { PrismModule } from "@1stdigital/prism-nestjs";
import { AppController } from "./app.controller";

@Module({
  imports: [
    PrismModule.forRoot({
      identifyToken: process.env.PRISM_IDENTIFY_TOKEN || "dev-key-123",
      baseUrl: "https://prism-gw.fd.xyz",
      routes: {
        "/api/premium": {
          price: 0.01, // $0.01 USD
          description: "Premium API",
        },
      },
    }),
  ],
  controllers: [AppController],
})
export class AppModule {}

// app.controller.ts
import { Controller, Get } from "@nestjs/common";
import { Payment, Payer } from "@1stdigital/prism-nestjs";

@Controller("api")
export class AppController {
  @Get("premium")
  @Payment() // Requires payment!
  getPremiumData(@Payer() payer: string) {
    return {
      message: "Premium content",
      payer,
    };
  }
}
```

***

## Configuration

### Module Options

```typescript theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
PrismModule.forRoot({
  identifyToken: "your-project-identify-token",
  baseUrl: "https://prism-gw.fd.xyz",
  debug: true,
  routes: {
    "/api/premium": {
      price: 0.01,
      description: "Premium API",
    },
    "/api/data/*": {
      price: 0.005,
      description: "Data API (wildcard)",
    },
  },
});
```

***

## Decorators

### @Payment() Decorator

```typescript theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
import { Controller, Get } from "@nestjs/common";
import { Payment } from "@1stdigital/prism-nestjs";

@Controller("api")
export class DataController {
  // Payment required
  @Get("premium")
  @Payment()
  getPremium() {
    return { data: "Premium content" };
  }

  // No payment required
  @Get("free")
  getFree() {
    return { data: "Free content" };
  }
}
```

### @Payer() Decorator

```typescript theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
import { Controller, Get } from "@nestjs/common";
import { Payment, Payer } from "@1stdigital/prism-nestjs";

@Controller("api")
export class DataController {
  @Get("premium")
  @Payment()
  getPremium(@Payer() payer: string) {
    // payer = '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb'
    return {
      message: "Premium data",
      payer,
    };
  }
}
```

***

## Accessing Payment Info

### Request Context

```typescript theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
import { Controller, Get, Req } from "@nestjs/common";
import { Payment } from "@1stdigital/prism-nestjs";
import { Request } from "express";

@Controller("api")
export class DataController {
  @Get("premium")
  @Payment()
  getPremium(@Req() request: Request) {
    // Access via request object
    const payer = request.payer;
    const payment = request.locals?.payment;

    return {
      message: "Premium data",
      payer,
      network: payment?.network,
    };
  }
}
```

***

## Settlement Validation

NestJS uses an **Interceptor** to validate settlement:

```typescript theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
// Internal implementation (automatic!)
@Injectable()
export class PrismSettlementInterceptor implements NestInterceptor {
  async intercept(context: ExecutionContext, next: CallHandler) {
    return next.handle().pipe(
      mergeMap(async (data) => {
        const response = context.switchToHttp().getResponse();
        const settlementResult = await this.settlementCallback(...);

        if (!settlementResult || !settlementResult.success) {
          // ❌ Settlement failed
          response.status(402);
          return {
            error: 'Payment settlement failed',
            details: settlementResult?.errorReason
          };
        }

        // ✅ Settlement succeeded
        response.header('X-PAYMENT-RESPONSE', settlementResult.transaction);
        return data;
      })
    );
  }
}
```

***

## Testing

```typescript theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
import { Test, TestingModule } from "@nestjs/testing";
import { INestApplication } from "@nestjs/common";
import * as request from "supertest";
import { AppModule } from "./app.module";

describe("PrismModule (e2e)", () => {
  let app: INestApplication;

  beforeAll(async () => {
    const moduleFixture: TestingModule = await Test.createTestingModule({
      imports: [AppModule],
    }).compile();

    app = moduleFixture.createNestApplication();
    await app.init();
  });

  afterAll(async () => {
    await app.close();
  });

  it("returns 402 without payment", () => {
    return request(app.getHttpServer())
      .get("/api/premium")
      .expect(402)
      .expect((res) => {
        expect(res.body.paymentRequired).toBe(true);
      });
  });

  it("returns 200 with valid payment", () => {
    const payment = JSON.stringify({
      scheme: "eip3009",
      signature: "0x" + "0".repeat(130),
    });

    return request(app.getHttpServer())
      .get("/api/premium")
      .set("X-PAYMENT", payment)
      .expect(200)
      .expect((res) => {
        expect(res.body.message).toBe("Premium content");
      });
  });
});
```

***

## Production Deployment

```typescript theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
// main.ts
import { NestFactory } from "@nestjs/core";
import { AppModule } from "./app.module";

async function bootstrap() {
  const app = await NestFactory.create(AppModule, {
    logger: ["error", "warn", "log"],
  });

  // Enable CORS if needed
  app.enableCors({
    origin: process.env.ALLOWED_ORIGINS?.split(","),
    credentials: true,
  });

  const port = parseInt(process.env.PORT || "3000");
  await app.listen(port);

  console.log(`NestJS + Prism running on port ${port}`);
}

bootstrap();
```
