Documentation Index
Fetch the complete documentation index at: https://docs.geekflare.com/llms.txt
Use this file to discover all available pages before exploring further.
This guide follows NestJS conventions: a dedicated GeekflareModule with an injectable GeekflareService, typed request DTOs, and .env config via ConfigModule. The examples use the official @geekflare/api-node SDK.
Prerequisites
Installation
bash pnpm add @geekflare/api-node @nestjs/config
bash npm install @geekflare/api-node @nestjs/config
Set your API key
GEEKFLARE_API_KEY=your_api_key_here
Never commit .env. Add it to .gitignore and use .env.example for
documentation.
Module setup
GeekflareService
Create a service that instantiates the client once and exposes typed methods.
src/geekflare/geekflare.service.ts
import { Injectable, OnModuleInit } from "@nestjs/common";
import { ConfigService } from "@nestjs/config";
import { GeekflareClient } from "@geekflare/api-node";
@Injectable()
export class GeekflareService implements OnModuleInit {
private client: GeekflareClient;
constructor(private readonly config: ConfigService) {}
onModuleInit() {
this.client = new GeekflareClient({
apiKey: this.config.getOrThrow<string>("GEEKFLARE_API_KEY"),
});
}
async webScrape(params: {
url: string;
renderJS?: boolean;
blockAds?: boolean;
format?: string;
}) {
return this.client.webScrape(params);
}
async search(params: {
query: string;
limit?: number;
location?: string;
source?: string;
time?: string;
includeDomains?: string[];
excludeDomains?: string[];
}) {
return this.client.search(params);
}
async screenshot(params: {
url: string;
type?: string;
fullPage?: boolean;
device?: string;
viewportWidth?: number;
viewportHeight?: number;
blockAds?: boolean;
hideCookie?: boolean;
quality?: number;
}) {
return this.client.screenshot(params);
}
}
GeekflareModule
src/geekflare/geekflare.module.ts
import { Module } from "@nestjs/common";
import { ConfigModule } from "@nestjs/config";
import { GeekflareService } from "./geekflare.service";
@Module({
imports: [ConfigModule],
providers: [GeekflareService],
exports: [GeekflareService],
})
export class GeekflareModule {}
AppModule
Register ConfigModule globally so ConfigService is available everywhere, then import GeekflareModule.
import { Module } from "@nestjs/common";
import { ConfigModule } from "@nestjs/config";
import { GeekflareModule } from "./geekflare/geekflare.module";
@Module({
imports: [ConfigModule.forRoot({ isGlobal: true }), GeekflareModule],
})
export class AppModule {}
Web Scraping
DTOs
src/scrape/dto/scrape.dto.ts
import { IsString, IsUrl, IsOptional, IsBoolean } from "class-validator";
export class ScrapeDto {
@IsUrl()
url: string;
@IsOptional()
@IsBoolean()
renderJS?: boolean;
@IsOptional()
@IsBoolean()
blockAds?: boolean;
@IsOptional()
@IsString()
format?: string;
}
Controller
src/scrape/scrape.controller.ts
import {
Body,
Controller,
Post,
HttpException,
HttpStatus,
} from "@nestjs/common";
import { GeekflareService } from "../geekflare/geekflare.service";
import { ScrapeDto } from "./dto/scrape.dto";
@Controller("scrape")
export class ScrapeController {
constructor(private readonly geekflare: GeekflareService) {}
@Post()
async scrape(@Body() dto: ScrapeDto) {
try {
return await this.geekflare.webScrape({
url: dto.url,
renderJS: dto.renderJS ?? true,
blockAds: dto.blockAds ?? true,
format: dto.format ?? "html,markdown",
});
} catch (error: any) {
throw new HttpException(
error.message ?? "Scrape failed",
error?.status ?? HttpStatus.INTERNAL_SERVER_ERROR,
);
}
}
}
Example request:
curl -X POST http://localhost:3000/scrape \
-H "Content-Type: application/json" \
-d '{ "url": "https://toscrape.com/", "renderJS": true }'
Search
DTOs
src/search/dto/search.dto.ts
import {
IsString,
IsOptional,
IsInt,
IsArray,
Min,
Max,
} from "class-validator";
export class SearchDto {
@IsString()
query: string;
@IsOptional()
@IsInt()
@Min(1)
@Max(100)
limit?: number;
@IsOptional()
@IsString()
location?: string;
@IsOptional()
@IsString()
source?: string;
@IsOptional()
@IsString()
time?: string; // "d" | "w" | "m"
@IsOptional()
@IsArray()
@IsString({ each: true })
includeDomains?: string[];
@IsOptional()
@IsArray()
@IsString({ each: true })
excludeDomains?: string[];
}
Controller
src/search/search.controller.ts
import {
Body,
Controller,
Post,
HttpException,
HttpStatus,
} from "@nestjs/common";
import { GeekflareService } from "../geekflare/geekflare.service";
import { SearchDto } from "./dto/search.dto";
@Controller("search")
export class SearchController {
constructor(private readonly geekflare: GeekflareService) {}
@Post()
async search(@Body() dto: SearchDto) {
try {
return await this.geekflare.search({
query: dto.query,
limit: dto.limit ?? 10,
location: dto.location ?? "us",
source: dto.source ?? "web",
time: dto.time,
includeDomains: dto.includeDomains,
excludeDomains: dto.excludeDomains,
});
} catch (error: any) {
throw new HttpException(
error.message ?? "Search failed",
error?.status ?? HttpStatus.INTERNAL_SERVER_ERROR,
);
}
}
}
Example request:
curl -X POST http://localhost:3000/search \
-H "Content-Type: application/json" \
-d '{ "query": "best NestJS practices", "limit": 5, "location": "us" }'
Screenshot
DTOs
src/screenshot/dto/screenshot.dto.ts
import {
IsString,
IsUrl,
IsOptional,
IsBoolean,
IsInt,
IsIn,
Min,
Max,
} from "class-validator";
export class ScreenshotDto {
@IsUrl()
url: string;
@IsOptional()
@IsIn(["png", "jpg", "webp"])
type?: string;
@IsOptional()
@IsBoolean()
fullPage?: boolean;
@IsOptional()
@IsIn(["desktop", "mobile"])
device?: string;
@IsOptional()
@IsInt()
viewportWidth?: number;
@IsOptional()
@IsInt()
viewportHeight?: number;
@IsOptional()
@IsBoolean()
blockAds?: boolean;
@IsOptional()
@IsBoolean()
hideCookie?: boolean;
@IsOptional()
@IsInt()
@Min(1)
@Max(100)
quality?: number;
}
Controller
src/screenshot/screenshot.controller.ts
import {
Body,
Controller,
Post,
HttpException,
HttpStatus,
} from "@nestjs/common";
import { GeekflareService } from "../geekflare/geekflare.service";
import { ScreenshotDto } from "./dto/screenshot.dto";
@Controller("screenshot")
export class ScreenshotController {
constructor(private readonly geekflare: GeekflareService) {}
@Post()
async screenshot(@Body() dto: ScreenshotDto) {
try {
return await this.geekflare.screenshot({
url: dto.url,
type: dto.type ?? "png",
fullPage: dto.fullPage ?? true,
device: dto.device ?? "desktop",
viewportWidth: dto.viewportWidth ?? 1280,
viewportHeight: dto.viewportHeight ?? 800,
blockAds: dto.blockAds ?? true,
hideCookie: dto.hideCookie ?? true,
quality: dto.quality ?? 90,
});
} catch (error: any) {
throw new HttpException(
error.message ?? "Screenshot failed",
error?.status ?? HttpStatus.INTERNAL_SERVER_ERROR,
);
}
}
}
Example request:
curl -X POST http://localhost:3000/screenshot \
-H "Content-Type: application/json" \
-d '{ "url": "https://example.com", "fullPage": true, "type": "png" }'
Wiring it all together
Register your feature modules in AppModule:
import { Module } from "@nestjs/common";
import { ConfigModule } from "@nestjs/config";
import { GeekflareModule } from "./geekflare/geekflare.module";
import { ScrapeController } from "./scrape/scrape.controller";
import { SearchController } from "./search/search.controller";
import { ScreenshotController } from "./screenshot/screenshot.controller";
@Module({
imports: [ConfigModule.forRoot({ isGlobal: true }), GeekflareModule],
controllers: [ScrapeController, SearchController, ScreenshotController],
})
export class AppModule {}
Enable ValidationPipe globally in main.ts to activate the DTO decorators:
import { NestFactory } from "@nestjs/core";
import { ValidationPipe } from "@nestjs/common";
import { AppModule } from "./app.module";
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalPipes(
new ValidationPipe({
whitelist: true, // strip unknown properties
forbidNonWhitelisted: true,
transform: true, // auto-transform to DTO types
}),
);
await app.listen(3000);
}
bootstrap();
Error handling
NestJS maps HttpException to the correct HTTP status automatically. For a global catch-all, add an exception filter:
src/filters/geekflare-exception.filter.ts
import {
ExceptionFilter,
Catch,
ArgumentsHost,
HttpException,
HttpStatus,
Logger,
} from "@nestjs/common";
@Catch()
export class GeekflareExceptionFilter implements ExceptionFilter {
private readonly logger = new Logger(GeekflareExceptionFilter.name);
catch(exception: unknown, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse();
const error = exception as any;
const status =
exception instanceof HttpException
? exception.getStatus()
: (error?.apiCode ?? HttpStatus.INTERNAL_SERVER_ERROR);
const message =
exception instanceof HttpException
? exception.message
: (error?.message ?? "Internal server error");
this.logger.error(`Geekflare API error [${status}]: ${message}`);
response.status(status).json({
statusCode: status,
error: message,
timestamp: new Date().toISOString(),
});
}
}
Register it in main.ts:
app.useGlobalFilters(new GeekflareExceptionFilter());
Common error codes
| Code | Meaning |
|---|
401 | Missing or invalid x-api-key |
402 | API credits exhausted |
403 | Endpoint not available on your plan |
404 | Wrong endpoint URL or HTTP method |
429 | Rate limit exceeded |
Next steps