Over the past few weeks I’ve been building GEO AI an Open Source ecosystem around AI Search Optimization.
The core idea is simple: if AI systems are going to read and reason about websites, then websites need a clearer way to expose structured content, crawler rules, and machine-readable signals.
Why a separate package for NestJS?
NestJS developers expect framework-native primitives: modules, DI, middleware, guards, interceptors, decorators. Asking them to manually call createGeoAI() and wire everything by hand would feel wrong.
So instead of a generic adapter, I built geo-ai-nest as a thin NestJS layer on top of geo-ai-core. All the GEO logic stays in the core package. The Nest package only adapts it to how Nest apps are structured.
Quick start
npm install geo-ai-nest geo-ai-core
Register the module:
import { Module } from '@nestjs/common';
import { GeoAIModule } from 'geo-ai-nest';
@Module({
imports: [
GeoAIModule.forRoot({
siteName: 'My App',
siteUrl: 'https://myapp.com',
provider: {
pages: [
{ title: 'About', url: '/about', description: 'About us' },
{ title: 'Pricing', url: '/pricing', description: 'Our plans' },
],
},
crawlers: 'all',
}),
],
})
export class AppModule {}
That's it. Your app now serves:
- GET /llms.txt — compact content index
- GET /llms-full.txt — full content with descriptions
- GET /.well-known/llms.txt — standard discovery path
- GET /robots-ai.txt — per-bot crawler rules
- No extra controllers needed.
Async configuration
Real apps usually load config from a database or config service. forRootAsync handles that:
GeoAIModule.forRootAsync({
imports: [ConfigModule],
useFactory: (config: ConfigService) => ({
siteName: config.get('SITE_NAME'),
siteUrl: config.get('SITE_URL'),
provider: { pages: [] }, // your ContentProvider here
crawlers: 'all',
injectLinkHeader: true,
cacheMaxAge: 7200,
}),
inject: [ConfigService],
}),
Detecting AI bots in your routes
The @IsAIBot() parameter decorator tells you which AI crawler is hitting your endpoint — or null if it's a regular user:
import { Controller, Get } from '@nestjs/common';
import { IsAIBot } from 'geo-ai-nest';
@Controller('api')
export class CatalogController {
@Get('products')
getProducts(@IsAIBot() bot: string | null) {
if (bot) {
console.log(`AI crawler detected: ${bot}`);
// return enriched response for AI consumption
}
return this.productService.findAll();
}
}
Protecting routes from non-AI traffic
GeoAIGuard restricts a route to verified AI bot requests only:
import { UseGuards } from '@nestjs/common';
import { GeoAIGuard } from 'geo-ai-nest';
@UseGuards(GeoAIGuard)
@Get('ai-only')
getAiContent() {
return { data: 'Only AI crawlers can see this' };
}
Why middleware for llms.txt instead of a controller?
This was a deliberate choice. Middleware runs before the routing layer, which means:
- It intercepts requests before guards, interceptors, or pipes run
- It works regardless of how the app's routing is structured
- It avoids conflicts with catch-all routes or global prefixes
The robots-ai.txt endpoint uses a controller because it's a standard REST resource that benefits from Nest's decorator pipeline.
Where it fits in GEO AI
At this point, the GEO AI ecosystem looks something like this:
- geo-ai-core — universal engine
- geo-ai-next — Next.js integration
- geo-ai-nest — NestJS integration
- geo-ai-cli — CLI for any Node.js project
The core package remains the center of gravity, and the framework packages are really adapters around it. The main GEO AI README now describes geo-ai-nest as the NestJS wrapper alongside the Next.js and CLI packages. 
I like this structure because it keeps the real logic in one place while still making adoption feel native inside each framework.
What I wanted from this release
More than anything, I wanted NestJS developers to be able to say:
“Install one package, register one module, and get the GEO surface area in a way that feels like Nest.”
That includes:
- dynamic module registration
- automatic llms routes
- a built-in robots endpoint
- DI-friendly service access
- optional route protection for AI bots
- optional response header injection
- decorators that fit how Nest apps are already written
That was the bar.

Top comments (0)