Synthèse automatique
Configure un environnement de développement local pour les intégrations de l’API Navan, avec mise en cache des jetons OAuth, structure de projet et préparation de fixtures de test.
Points forts
- Fournit une arborescence de projet claire qui sépare client API, types, tests, journaux et fichiers d’environnement.
- Montre une implémentation concrète du cache de jetons avec expiration et permissions de fichier restrictives.
- Inclut les bonnes pratiques de base pour ne pas versionner les secrets, les journaux et le cache de jetons.
- Tient compte de l’absence de sandbox Navan en proposant des fixtures pour limiter les appels à la production.
Limites
- La journalisation des requêtes et l’utilisation des fixtures sont surtout décrites par la structure et les variables d’environnement, avec peu de code fourni.
- Le skill suppose que l’authentification OAuth et un client API Navan existent déjà.
- Comme Navan n’a pas de sandbox, une mauvaise configuration peut envoyer des requêtes vers la production avec de vraies données.
À utiliser au démarrage d’un projet d’intégration Navan ou pour structurer un environnement local plus sûr pendant le débogage.
À éviter si vous cherchez un guide complet de tests hors ligne ou si vous ne disposez pas encore d’identifiants OAuth Navan valides.
Analyse de sécurité
PrudenceThe skill is a legitimate local dev-setup guide with no malicious payloads, but it operates against a production-only API, caches bearer tokens in plaintext, and instructs recording real booking/user responses into fixture files without PII handling or gitignore coverage — worth a caution rather than risky, since everything is transparent and aimed at reducing production calls.
- •Persists the OAuth access token in plaintext on disk at `.token-cache` (mode 0o600 and gitignored, but still a bearer token at rest with no encryption/expiry purging beyond timestamp check).
- •Step 7 `record-fixtures.ts` deliberately calls live production Navan endpoints (the skill notes Navan has no sandbox) and writes raw response bodies — booking records with traveler names/dates and `/v1/users` data — into `tests/fixtures/`. Fixtures are not added to `.gitignore` and no PII scrubbing step is provided, so real customer data can be committed to a repo.
- •Request logger writes a 200-character response preview plus full URL to `logs/navan-api.log`; redaction only covers `client_secret` in query strings, so response-derived PII can land in logs (gitignored, but no rotation/purge).
- •Several instructed commands fall outside the declared `Bash(npm:*)` tool scope (`echo ".env" >> .gitignore`, `tail -f logs/navan-api.log | python3 -m json.tool`), indicating the declared tool list understates what the skill asks the agent to run.
- •No destructive, exfiltration, obfuscation, or safety-disabling behavior; network use is confined to the documented Navan API host taken from env.
- •Step 7 ('Recording Fixtures from Production') pulls real /v1/bookings and /v1/users responses, which contain traveler PII (names, itineraries), and writes them verbatim into tests/fixtures/ — a directory that is not covered by the .gitignore guidance, so production personal data can easily be committed to a repo. No scrubbing/anonymization step is included.
- •The OAuth access token is persisted in plaintext to .token-cache on disk (mitigated by mode 0o600 and a gitignore instruction, but still a recoverable bearer credential on the workstation).
- •Request logging writes a 200-char response_preview of every API response to logs/navan-api.log; client_secret in URLs is redacted, but response bodies (PII) are not, and the log tail command outputs them to the terminal.
- •Navan has no sandbox, so the workflow's 'live' scripts send real credentials and traffic to the production API; the skill does document this and offers an offline-fixture mode as mitigation.
Exemples
Set up a local Navan API dev environment in this repository with token caching, request logging, and a .env.example file.Create a .env.example, update .gitignore, scaffold the Navan integration project structure, and add the token cache implementation.Help me debug Navan API calls locally using cached OAuth tokens and fixtures so I do not hit production on every run.name: navan-local-dev-loop description: 'Set up a local development environment for Navan API integrations with token caching and request logging.
Use when starting a new Navan project or debugging API issues locally.
Trigger with "navan local dev", "navan dev setup", "navan local dev loop", "navan dev environment".
' allowed-tools: Read, Write, Edit, Bash(npm:*), Grep version: 1.8.0 license: MIT author: Jeremy Longshore jeremy@intentsolutions.io tags:
- saas
- navan
- travel compatibility: Designed for Claude Code
Navan Local Dev Loop
Overview
Configure a local development environment for Navan API integrations with token caching, request logging, and mock fixtures. Navan has no sandbox — all API calls hit production, making a structured local setup essential.
Purpose: Establish a safe local dev workflow that minimizes production API calls during iteration.
Prerequisites
- Completed
navan-install-authwith working OAuth 2.0 credentials - Node.js 18+ with
tsxfor TypeScript execution .envfile withNAVAN_CLIENT_ID,NAVAN_CLIENT_SECRET,NAVAN_BASE_URL
Instructions
Step 1: Project Structure
Set up a clean project layout that separates concerns:
my-navan-integration/
├── .env # Credentials (NEVER commit)
├── .env.example # Template for teammates
├── .gitignore # Must include .env, .token-cache, logs/
├── src/
│ ├── navan-client.ts # API wrapper (from navan-sdk-patterns)
│ ├── navan-types.ts # Response interfaces
│ └── index.ts # Entry point
├── tests/
│ ├── fixtures/ # Recorded API responses for offline dev
│ │ ├── bookings.json
│ │ └── users.json
│ └── navan-client.test.ts
├── logs/ # Request/response logs (gitignored)
├── .token-cache # Cached OAuth token (gitignored)
├── package.json
└── tsconfig.json
Step 2: Environment Configuration
Create .env.example as a safe template and enforce .gitignore:
# .env.example — commit this file, NOT .env
NAVAN_CLIENT_ID="your-client-id"
NAVAN_CLIENT_SECRET="your-client-secret"
NAVAN_BASE_URL="https://api.navan.com"
NAVAN_LOG_REQUESTS="true"
NAVAN_USE_FIXTURES="false"
# .gitignore additions for Navan projects
echo ".env" >> .gitignore
echo ".token-cache" >> .gitignore
echo "logs/" >> .gitignore
Step 3: Token Cache Implementation
Persist tokens to disk to avoid hitting /ta-auth/oauth/token on every script run:
// src/token-cache.ts
import { readFileSync, writeFileSync, existsSync } from 'fs';
interface CachedToken {
access_token: string;
expires_at: number; // Unix timestamp in ms
}
const CACHE_FILE = '.token-cache';
export function getCachedToken(): string | null {
if (!existsSync(CACHE_FILE)) return null;
try {
const cached: CachedToken = JSON.parse(readFileSync(CACHE_FILE, 'utf-8'));
if (Date.now() < cached.expires_at - 60_000) {
return cached.access_token;
}
} catch { /* corrupt cache, re-auth */ }
return null;
}
export function setCachedToken(token: string, expiresIn: number): void {
const cached: CachedToken = {
access_token: token,
expires_at: Date.now() + expiresIn * 1000,
};
writeFileSync(CACHE_FILE, JSON.stringify(cached), { mode: 0o600 });
}
Integrate with authentication:
import { getCachedToken, setCachedToken } from './token-cache';
async function getNavanToken(): Promise<string> {
const cached = getCachedToken();
if (cached) return cached;
const response = await fetch(`${process.env.NAVAN_BASE_URL}/ta-auth/oauth/token`, {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
grant_type: 'client_credentials',
client_id: process.env.NAVAN_CLIENT_ID!,
client_secret: process.env.NAVAN_CLIENT_SECRET!,
}),
});
if (!response.ok) throw new Error(`Auth failed: ${response.status}`);
const data = await response.json();
setCachedToken(data.access_token, data.expires_in);
return data.access_token;
}
Step 4: Request Logger
Log all API requests and responses for debugging without exposing secrets:
// src/request-logger.ts
import { appendFileSync, mkdirSync, existsSync } from 'fs';
const LOG_DIR = 'logs';
const LOG_FILE = `${LOG_DIR}/navan-api.log`;
export function logRequest(method: string, url: string, status: number, durationMs: number, body?: string): void {
if (process.env.NAVAN_LOG_REQUESTS !== 'true') return;
if (!existsSync(LOG_DIR)) mkdirSync(LOG_DIR, { recursive: true });
const entry = {
timestamp: new Date().toISOString(),
method,
url: url.replace(/client_secret=[^&"]+/g, 'client_secret=***'),
status,
duration_ms: durationMs,
response_preview: body ? body.substring(0, 200) : undefined,
};
appendFileSync(LOG_FILE, JSON.stringify(entry) + '\n');
}
// Usage in API wrapper
const start = Date.now();
const response = await fetch(url, options);
const body = await response.text();
logRequest('GET', url, response.status, Date.now() - start, body);
Step 5: Mock Fixtures for Offline Development
Record real API responses and replay them during development:
// tests/fixtures/bookings.json
{
"data": [
{
"uuid": "trip-001-abc-def",
"traveler_name": "Jane Smith",
"origin": "SFO",
"destination": "JFK",
"departure_date": "2026-04-01",
"return_date": "2026-04-05",
"booking_status": "confirmed",
"booking_type": "flight"
}
]
}
Use fixtures when NAVAN_USE_FIXTURES is set:
// src/fixture-loader.ts
import { readFileSync } from 'fs';
import { join } from 'path';
const FIXTURE_DIR = join(__dirname, '..', 'tests', 'fixtures');
export function loadFixture<T>(endpoint: string): T | null {
if (process.env.NAVAN_USE_FIXTURES !== 'true') return null;
const filename = endpoint.replace(/^\//, '').replace(/\//g, '_') + '.json';
try {
return JSON.parse(readFileSync(join(FIXTURE_DIR, filename), 'utf-8'));
} catch {
return null;
}
}
// In the API wrapper, check fixtures first
async function request<T>(endpoint: string): Promise<T> {
const fixture = loadFixture<T>(endpoint);
if (fixture) return fixture;
// ... real API call
}
Step 6: Dev Scripts
Configure package.json for a fast iteration loop:
{
"scripts": {
"dev": "tsx watch src/index.ts",
"dev:offline": "NAVAN_USE_FIXTURES=true tsx watch src/index.ts",
"test": "vitest",
"test:live": "NAVAN_USE_FIXTURES=false vitest",
"record": "NAVAN_LOG_REQUESTS=true tsx src/record-fixtures.ts",
"logs": "tail -f logs/navan-api.log | python3 -m json.tool"
}
}
Step 7: Recording Fixtures from Production
Create a one-time script to capture real responses for offline use:
// src/record-fixtures.ts
import { writeFileSync, mkdirSync } from 'fs';
const FIXTURE_DIR = 'tests/fixtures';
mkdirSync(FIXTURE_DIR, { recursive: true });
const token = await getNavanToken();
const endpoints = ['/v1/bookings?page=0&size=50', '/v1/users'];
for (const endpoint of endpoints) {
const response = await fetch(`${process.env.NAVAN_BASE_URL}${endpoint}`, {
headers: { Authorization: `Bearer ${token}` },
});
if (response.ok) {
const data = await response.json();
const filename = endpoint.replace(/^\//, '') + '.json';
writeFileSync(`${FIXTURE_DIR}/${filename}`, JSON.stringify(data, null, 2));
console.log(`Recorded ${endpoint} -> ${filename}`);
} else {
console.error(`Failed to record ${endpoint}: ${response.status}`);
}
}
Output
Successful setup produces:
- A project scaffold with proper secret isolation (.env, .gitignore)
- Token caching that avoids redundant auth calls to production
- Request/response logging for debugging with secret redaction
- Mock fixtures for offline development without production API calls
- Dev scripts for live, offline, and recording modes
Error Handling
| Error | Code | Cause | Solution |
|-------|------|-------|----------|
| Unauthorized | 401 | Cached token expired | Delete .token-cache and re-authenticate |
| Forbidden | 403 | Credentials lack required scope | Regenerate credentials with proper permissions |
| Not found | 404 | Fixture file missing for endpoint | Record fixtures with npm run record |
| Rate limited | 429 | Too many dev iterations hitting production | Switch to npm run dev:offline |
| Server error | 500 | Navan service issue | Use fixtures; retry later |
| Maintenance | 503 | Navan downtime | Use NAVAN_USE_FIXTURES=true for offline mode |
Examples
Typical dev workflow:
# 1. First time: record fixtures from production
npm run record
# 2. Daily development: use offline mode
npm run dev:offline
# 3. Integration testing: hit production
npm run test:live
# 4. Debug a failure: check logs
npm run logs
Resources
- Navan Help Center — primary documentation hub
- Navan TMC API Docs — API reference
- Navan Booking Data — booking data export docs
- Navan Security — compliance certifications
Next Steps
With your local dev environment running, see navan-sdk-patterns for production-grade wrapper patterns, or navan-common-errors when you encounter API failures during development.
Du fichier au premier essai
Le téléchargement contient exactement le contenu affiché sur cette fiche. Cette copie peut différer de la dernière version du dépôt source.
Consulter la source et ses fichiers annexesCe téléchargement contient uniquement SKILL.md. Les scripts, références, modèles et dépendances éventuels ne sont pas inclus. Récupérez les fichiers nécessaires depuis la source en conservant leur arborescence avant de poursuivre.
1. Télécharger le fichier
Télécharger SKILL.mdDepuis la racine d’un projet de test, enregistrez le fichier sous .skill-downloads/navan-local-dev-environment-mts7ikop/SKILL.md, ou utilisez la commande suivante. Ce dossier sert à examiner la skill avant son installation.
Commande pour macOS/Linux ou WSL : un shell POSIX et curl sont nécessaires. Sous Windows sans WSL, utilisez le bouton de téléchargement et créez le dossier manuellement.
mkdir -p '.skill-downloads/navan-local-dev-environment-mts7ikop' && curl --fail --show-error --location 'https://skills-guides.com/api/skills/navan-local-dev-environment-mts7ikop/download' --output '.skill-downloads/navan-local-dev-environment-mts7ikop/SKILL.md.part' && mv '.skill-downloads/navan-local-dev-environment-mts7ikop/SKILL.md.part' '.skill-downloads/navan-local-dev-environment-mts7ikop/SKILL.md'2. Vérifier les prérequis
Lisez SKILL.md avant de l’utiliser. Si le fichier cite des scripts ou services externes, examinez-les et préparez les accès nécessaires dans votre environnement de test.
Environnement déclaré par l’auteur : Designed for Claude Code
Outils déclarés par l’auteur (à vérifier dans votre agent) : Read, Write, Edit, Bash(npm:*), Grep
Extrait original : Prerequisites
- Completed
navan-install-authwith working OAuth 2.0 credentials - Node.js 18+ with
tsxfor TypeScript execution .envfile withNAVAN_CLIENT_ID,NAVAN_CLIENT_SECRET,NAVAN_BASE_URL
Les plateformes de la fiche sont des indications du catalogue. Les chemins ci-dessous suivent la documentation des outils ; ils ne prouvent pas que cette skill y a été exécutée. Aucune version minimale de ces outils n’est attestée pour cette fiche.
3. Charger la skill
Après vérification, copiez le dossier complet dans le projet avec l’une des commandes ci-dessous, puis ouvrez une nouvelle session de l’outil. La commande s’arrête si un dossier du même nom existe déjà : comparez les fichiers avant de le remplacer.
Claude Code
test -f '.skill-downloads/navan-local-dev-environment-mts7ikop/SKILL.md' && test -s '.skill-downloads/navan-local-dev-environment-mts7ikop/SKILL.md' && mkdir -p '.claude/skills' && test ! -e '.claude/skills/navan-local-dev-loop' && cp -R '.skill-downloads/navan-local-dev-environment-mts7ikop' '.claude/skills/navan-local-dev-loop'OpenSkills
OpenSkills doit déjà être installé. La procédure locale install/read est contrôlée avec OpenSkills 1.5.0, qui exige Node.js ≥ 20.6.0. Aucun slug du catalogue n’est résolu automatiquement.
Installation officielle d’OpenSkillsopenskills install './.skill-downloads/navan-local-dev-environment-mts7ikop'openskills read 'navan-local-dev-environment-mts7ikop'La lecture doit afficher le contenu du fichier installé et son dossier de base. Elle ne lance ni la skill ni ses dépendances.
Pour un agent qui lit AGENTS.md, synchronisez ensuite l’index. Cette commande modifie AGENTS.md : relisez le diff avant de l’utiliser.
openskills syncMode manuel ou autre outil : joignez SKILL.md à une conversation, ou ouvrez-le dans un agent capable de lire les fichiers. Ajoutez les ressources annexes nécessaires. Il n’existe pas de dossier universel reconnu par tous les outils.
4. Faire un premier essai
Utilisez une entrée courte et fictive : un endpoint d’API, un petit document ou un brief d’interface. Dans une nouvelle conversation, joignez le fichier ou donnez son chemin à l’agent, puis adaptez cette demande :
Lis .skill-downloads/navan-local-dev-environment-mts7ikop/SKILL.md et applique ses instructions à cet exemple : [décrire une entrée de test]. Indique les fichiers, dépendances ou accès qui manquent. Produis le résultat demandé par la skill sans inventer de données ni exécuter de script non examiné.Résultat attendu : une sortie correspondant aux instructions de la skill, ou une liste précise des prérequis manquants. Une simple confirmation de lecture ne valide pas le premier usage. Si une ressource externe est indisponible, ce parcours reste à tester.
Bonnes pratiques React Hook Form
Developpement
Maîtrisez React Hook Form : évitez les anti-patterns, gérez les abonnements, et traitez correctement reset, dirty state et entrées contrôlées.
Migration Emotion vers Mantine
Developpement
Migrer les styled-components Emotion vers des composants Mantine avec style props et modules CSS. Pour convertir .styled.tsx ou supprimer les imports @emotion.
Détecteur de dérive de périmètre
Developpement
Analyse les diffs git par rapport à une intention pour détecter la dérive de périmètre, fichiers non liés et PR trop larges, puis suggère de garder, diviser ou justifier.