NomikNomik

Parser Extractors

Complete reference for all 37 extractors across 13 languages — with code examples showing exactly what Nomik detects.

Nomik's parser uses Tree-sitter to convert source code into AST nodes, then runs 37 extractors to produce typed graph nodes and edges. Config files are parsed via a dedicated regex-based config-file-parser.ts without tree-sitter. All TypeScript/JavaScript extractors are import-aware — they resolve receiver variables from actual imports, not hardcoded names.

Supported Languages

Code Languages (Tree-sitter)

LanguageGrammarExtensionsScope
TypeScripttree-sitter-typescript.ts, .tsxAll 37 extractors
JavaScripttree-sitter-typescript.js, .jsx, .mjs, .cjsAll 37 extractors
Pythontree-sitter-python.py, .pywFunctions, classes, imports, calls + runtime extractors
Rusttree-sitter-rust.rsFunctions, structs/enums/traits, use, calls
MarkdownCustom regex parser.mdSections (h1-h6 headings)
SQLCustom regex parser.sqlCREATE TABLE, ALTER TABLE, column definitions
C#/Python migrationsCustom regex parser.cs, .pyEntity Framework, Django, Alembic migration schema

Config File Languages (regex via config-file-parser.ts)

LanguageExtensionsWhat's Extracted
YAML.yml, .yamlDocker Compose services, K8s manifests, GitHub Actions jobs, GitLab CI stages, CloudFormation resources, Prometheus alert rules, OpenAPI specs
Terraform.tf, .tfvarsResources, variables, modules, outputs
GraphQL.graphql, .gqlTypes, queries, mutations, subscriptions
DockerfileDockerfile, Dockerfile.*Base images, exposed ports, build stages
.env.env, .env.*Environment variable definitions (deduplicated with process.env references in code)
JSON configpackage.jsonDependencies, devDependencies, OpenAPI specs, Grafana dashboards

Config files are automatically discovered during nomik scan. No extra configuration needed — Nomik detects file types by name and extension and routes them to the correct extractor.

Code Extractors

Functions

What Nomik extracts
// → FunctionNode: name="processPayment", async=true, exported=true,
//   params="orderId: string, amount: number", returnType="Promise<void>",
//   startLine=1, endLine=5, bodyHash="sha256:a1b2c3..."
export async function processPayment(orderId: string, amount: number): Promise<void> {
  const order = await getOrder(orderId);
  await createCharge(order, amount);
  eventBus.emit('payment.completed', { orderId });
}

Extracted properties: name, params, returnType, async, exported, generator, decorators, bodyHash (SHA-256, whitespace-normalized), startLine, endLine, filePath.

Classes

What Nomik extracts
// → ClassNode: name="PaymentService", abstract=false
// → EXTENDS edge to BaseService
// → IMPLEMENTS edge to IPaymentProvider
export class PaymentService extends BaseService implements IPaymentProvider {
  async process(orderId: string) { /* ... */ }
}

Imports and Exports

What Nomik extracts
// → ImportInfo: source="@prisma/client", specifiers=["PrismaClient"], isDefault=false
import { PrismaClient } from '@prisma/client';

// → ImportInfo: source="./payment", specifiers=["processPayment"], isDynamic=false
import { processPayment } from './payment';

// → ExportInfo: re-export resolution, barrel file detection
export { validateUser } from './auth';
export * from './utils';

Calls (with Confidence Scoring)

What Nomik extracts
function checkout(orderId: string) {
  // → CALLS edge: checkout → validateUser (confidence: 1.0, same-file)
  validateUser(req.user);

  // → CALLS edge: checkout → processPayment (confidence: 0.95, import-resolved)
  processPayment(orderId, amount);

  // → CALLS edge: checkout → orderService.getOrder (confidence: 0.95, obj.method())
  const order = orderService.getOrder(orderId);

  // → CALLS edge: checkout → sendNotification (confidence: 0.60, global-filtered)
  sendNotification(orderId);
}
ResolutionConfidenceDescription
Same-file1.0Caller and callee in the same file
File-context0.90Resolved within file scope
Import-resolved0.95Resolved via import statement
Global-filtered0.60Resolved via global function name match (single match)

API Extractors

Routes

What Nomik extracts
// → RouteNode: method="POST", path="/api/checkout", handler="checkoutHandler"
// → HANDLES edge: Route → checkoutHandler
app.post('/api/checkout', checkoutHandler);

// → RouteNode: method="GET", path="/api/users/:id"
router.get('/api/users/:id', (req, res) => { /* ... */ });

// NestJS decorators
// → RouteNode: method="POST", path="/payments", handler="create"
@Post('/payments')
async create(@Body() dto: CreatePaymentDto) { /* ... */ }

Supported: Express, Fastify, NestJS, Koa, Hapi, Next.js, Nuxt, tRPC, gRPC, GraphQL resolvers.

External API Calls

What Nomik extracts
import axios from 'axios';
import { ofetch } from 'ofetch';

// → ExternalAPINode: name="stripe", url="https://api.stripe.com/v1/charges"
// → CALLS_EXTERNAL edge: processPayment → stripe
await axios.post('https://api.stripe.com/v1/charges', data);

// → CALLS_EXTERNAL edge: fetchUsers → jsonplaceholder
const users = await ofetch('https://jsonplaceholder.typicode.com/users');

// Built-in globals always detected
// → CALLS_EXTERNAL edge: getConfig → config-api
await fetch('/api/config');

Import-aware detection: axios, ky, got, node-fetch, ofetch, undici, superagent, fetch(), $fetch().

Data Extractors

Database Operations

What Nomik extracts — Prisma
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();

// → WRITES_TO edge: createUser → DBTable:user (operation: INSERT)
await prisma.user.create({ data: { name, email } });

// → READS_FROM edge: getUsers → DBTable:user (operation: SELECT)
const users = await prisma.user.findMany({ where: { active: true } });
What Nomik extracts — Supabase
import { createClient } from '@supabase/supabase-js';
const supabase = createClient(url, key);

// → WRITES_TO edge (correctly classified as INSERT despite terminal .select())
await supabase.from('orders').insert({ userId, total }).select();

// → READS_FROM edge
const { data } = await supabase.from('products').select('*').eq('active', true);

Supported ORMs: Prisma, Supabase, Knex, TypeORM, Drizzle, pg, mysql2, raw SQL.

Database Schema

What Nomik extracts — SQL migrations
-- → DBTableNode: name="users"
-- → DBColumnNode: name="id", type="SERIAL", primaryKey=true
-- → DBColumnNode: name="email", type="VARCHAR(255)", nullable=false
CREATE TABLE users (
  id SERIAL PRIMARY KEY,
  email VARCHAR(255) NOT NULL,
  created_at TIMESTAMP DEFAULT NOW()
);
What Nomik extracts — Django migrations
# → DBTableNode: name="orders"
# → DBColumnNode: name="total", type="DecimalField"
class Migration(migrations.Migration):
    operations = [
        migrations.CreateModel(
            name='Order',
            fields=[
                ('id', models.AutoField(primary_key=True)),
                ('total', models.DecimalField(max_digits=10)),
            ],
        ),
    ]

Redis

What Nomik extracts
import Redis from 'ioredis';
const redis = new Redis();

// → WRITES_TO edge: cacheUser → DBTable:user:* (schema=redis, operation=WRITE)
await redis.set(`user:${userId}`, JSON.stringify(user));

// → READS_FROM edge: getCachedUser → DBTable:user:* (schema=redis, operation=READ)
const cached = await redis.get(`user:${userId}`);

Infrastructure Extractors

Queue Jobs

What Nomik extracts
import { Queue, Worker } from 'bullmq';

const emailQueue = new Queue('email-notifications');

// → PRODUCES_JOB edge: sendWelcome → QueueJob:email-notifications
await emailQueue.add('welcome', { userId, template: 'welcome' });

// → CONSUMES_JOB edge: emailWorker → QueueJob:email-notifications
const worker = new Worker('email-notifications', async (job) => {
  await sendEmail(job.data);
});

Supported: Bull, BullMQ, Bee-Queue, Agenda, pg-boss.

Metrics

What Nomik extracts
import { Counter, Histogram } from 'prom-client';

// → MetricNode: name="http_requests_total", type="Counter"
const requestCounter = new Counter({
  name: 'http_requests_total',
  help: 'Total HTTP requests',
  labelNames: ['method', 'status'],
});

// → USES_METRIC edge: handleRequest → Metric:http_requests_total
function handleRequest(req: Request) {
  requestCounter.labels(req.method, '200').inc();
}

Tracing (OpenTelemetry)

What Nomik extracts
import { trace } from '@opentelemetry/api';

const tracer = trace.getTracer('payment-service');

// → SpanNode: name="process-payment"
// → STARTS_SPAN edge: processPayment → Span:process-payment
function processPayment(orderId: string) {
  tracer.startActiveSpan('process-payment', async (span) => {
    // ... payment logic
    span.end();
  });
}

Supported: @opentelemetry/api, dd-trace (Datadog), @sentry/node.

Message Brokers

What Nomik extracts
import { Kafka } from 'kafkajs';

const kafka = new Kafka({ brokers: ['localhost:9092'] });
const producer = kafka.producer();

// → PRODUCES_MESSAGE edge: publishOrder → Topic:order-events
await producer.send({ topic: 'order-events', messages: [{ value: JSON.stringify(order) }] });

const consumer = kafka.consumer({ groupId: 'order-processor' });
// → CONSUMES_MESSAGE edge: orderHandler → Topic:order-events
await consumer.subscribe({ topic: 'order-events' });

Supported: KafkaJS, amqplib (RabbitMQ), NATS, AWS SQS/SNS, Google Pub/Sub.

Events and WebSockets

What Nomik extracts
// → EventNode: name="payment.completed"
// → EMITS edge: processPayment → Event:payment.completed
eventBus.emit('payment.completed', { orderId });

// → LISTENS_TO edge: sendReceipt → Event:payment.completed
eventBus.on('payment.completed', sendReceipt);

// Socket.io room/namespace detection
// → EventNode with room="admin-room"
socket.to('admin-room').emit('notification', data);
io.of('/admin').on('connection', handler);

Cron Jobs

What Nomik extracts
import cron from 'node-cron';

// → CronJobNode: name="cleanup", schedule="0 0 * * *"
// → SCHEDULES edge: cleanupJob → CronJob:cleanup
cron.schedule('0 0 * * *', async () => {
  await cleanupExpiredSessions();
});

// NestJS @Cron decorator
// → CronJobNode: name="handleDailyReport", schedule="0 8 * * *"
@Cron('0 8 * * *')
async handleDailyReport() { /* ... */ }

Config Extractors

Docker and Kubernetes

What Nomik extracts — Dockerfile
# → Config metadata: baseImage="node:20-alpine", exposedPort=3000
FROM node:20-alpine
EXPOSE 3000
CMD ["node", "dist/index.js"]
What Nomik extracts — docker-compose.yml
# → Service nodes with port mappings and dependencies
services:
  api:
    build: .
    ports: ["3000:3000"]
    depends_on: [neo4j, redis]
  neo4j:
    image: neo4j:5-community

CI/CD

What Nomik extracts — GitHub Actions
# → CronJobNode for each job, steps with action references
name: CI
on: [push, pull_request]
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm test
  quality:
    runs-on: ubuntu-latest
    steps:
      - run: nomik ci

Terraform

What Nomik extracts — main.tf
# → ClassNode: name="aws_instance.api", type="resource"
resource "aws_instance" "api" {
  ami           = var.ami_id
  instance_type = "t3.medium"
}

# → EnvVarNode: name="ami_id"
variable "ami_id" {
  type = string
}

Environment Variables

What Nomik extracts
// → EnvVarNode: name="DATABASE_URL", required=true
const dbUrl = process.env.DATABASE_URL!;

// → EnvVarNode: name="PORT", defaultValue="3000"
const port = process.env.PORT ?? '3000';

// → EnvVarNode: name="LOG_LEVEL", defaultValue="info"
const logLevel = process.env['LOG_LEVEL'] || 'info';
What Nomik extracts — Python
# → EnvVarNode: name="SECRET_KEY"
secret = os.environ['SECRET_KEY']

# → EnvVarNode: name="DEBUG", defaultValue="false"
debug = os.getenv('DEBUG', 'false')

Security Extractors

Secret Detection

What Nomik detects (and flags)
// → SecurityIssueNode: type="aws_key", severity="critical"
const awsKey = 'AKIAIOSFODNN7EXAMPLE';

// → SecurityIssueNode: type="github_token", severity="high"
const token = 'ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';

// → SecurityIssueNode: type="stripe_key", severity="critical"
const stripe = 'sk_live_xxxxxxxxxxxxxxxxxxxx';

// → SecurityIssueNode: type="private_key", severity="critical"
const key = '-----BEGIN RSA PRIVATE KEY-----';

// → SecurityIssueNode: type="basic_auth_url", severity="high"
const url = 'https://admin:password@api.example.com';

Skips: comments, test files, mock data, placeholder values (xxx, example, test).

Feature Flags

What Nomik extracts
import { LDClient } from 'launchdarkly-node-server-sdk';

// → EnvVarNode: name="new-checkout-flow", kind="feature_flag"
const showNewCheckout = await ldClient.variation('new-checkout-flow', user, false);

// Also detects env-based flags
// → EnvVarNode: name="FEATURE_NEW_CHECKOUT"
if (process.env.FEATURE_NEW_CHECKOUT === 'true') { /* ... */ }

Python Extractors

What Nomik extracts
# Functions with typed parameters (self/cls excluded)
# → FunctionNode: name="process_order", async=true, params="order_id: str, db: Session"
async def process_order(order_id: str, db: Session) -> Order:
    # Celery tasks
    # → PRODUCES_JOB edge → QueueJob:send_notification
    send_notification.delay(order_id)

    # Redis operations
    # → WRITES_TO edge → DBTable:order (schema=redis)
    redis_client.set(f"order:{order_id}", json.dumps(data))

    # Prometheus metrics
    # → USES_METRIC edge → Metric:orders_processed
    orders_processed.inc()

    # OpenTelemetry spans
    # → STARTS_SPAN edge → Span:process_order
    with tracer.start_span("process_order"):
        pass

    # Message brokers (confluent_kafka, pika, nats)
    # → PRODUCES_MESSAGE edge → Topic:order-events
    producer.produce('order-events', value=json.dumps(event))

Rust Extractors

What Nomik extracts
// → FunctionNode: name="handle_request", async=true, visibility="pub"
pub async fn handle_request(req: Request) -> Response {
    let user = db::get_user(req.user_id).await;
    let response = process(user).await;
    response
}

// → ClassNode: name="PaymentService" (struct)
pub struct PaymentService {
    db: DatabasePool,
    stripe: StripeClient,
}

// → ClassNode: name="Payment" (enum)
pub enum Payment {
    CreditCard { number: String },
    BankTransfer { iban: String },
}

// → ClassNode: name="Processable" (trait, abstract=true)
pub trait Processable {
    async fn process(&self) -> Result<(), Error>;
}

// → ImportInfo: use declarations
use crate::services::payment::PaymentService;
use std::collections::HashMap;

Cross-File Resolution

After per-file extraction, the parser resolves cross-file relationships:

  • Cross-file CALLS — Multi-map resolution (Map<string, string[]>) handles duplicate function names. Two-layer defense for name collisions: local shadow check + importedFileIds filter. Method calls (obj.method()) skip shadow check since the receiver disambiguates
  • Barrel re-exportsexport * from and export { x } from are followed transitively via BFS to resolve all re-exported symbols
  • Intra-file CALLS — Local function calls, variable array aliases, declaration aliases
  • Route handlingHANDLES edges, EXTENDS/IMPLEMENTS, framework entry points (Next.js, Nuxt)
  • Path alias resolution — tsconfig.json/jsconfig.json path alias resolution (@/*, ~/*) for monorepos