Development Environment

Setting up and troubleshooting local development environment issues.

Development Environment

Guide to setting up and troubleshooting your local development environment.

Environment Setup

Node.js and npm Issues

Version Management:

# Check Node.js version
node --version
npm --version

# Use Node Version Manager (nvm)
# Install nvm (Linux/Mac)
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash

# Install and use specific Node.js version
nvm install 18
nvm use 18
nvm alias default 18

# Windows - use nvm-windows
# Download from: https://github.com/coreybutler/nvm-windows

# Check installed versions
nvm list

# Use project-specific Node.js version
# Create .nvmrc file
echo "18.17.0" > .nvmrc
nvm use

Package Management Issues:

# Clear npm cache
npm cache clean --force

# Delete node_modules and reinstall
rm -rf node_modules package-lock.json
npm install

# Fix permission issues (Linux/Mac)
sudo chown -R $(whoami) ~/.npm
sudo chown -R $(whoami) /usr/local/lib/node_modules

# Use npm audit to fix vulnerabilities
npm audit
npm audit fix
npm audit fix --force

# Update all packages
npm update
npx npm-check-updates -u
npm install

Environment Variables Configuration

# Create environment files
touch .env.local
touch .env.development
touch .env.production

# .env.local (for local development)
NEXT_PUBLIC_SUPABASE_URL=your_local_supabase_url
NEXT_PUBLIC_SUPABASE_ANON_KEY=your_local_anon_key
SUPABASE_SERVICE_ROLE_KEY=your_service_role_key

# Database URL for local development
DATABASE_URL=postgresql://postgres:password@localhost:54322/postgres

# Development flags
NEXT_PUBLIC_DEBUG=true
NEXT_PUBLIC_ENVIRONMENT=development

Environment Variable Validation:

// lib/env.ts
import { z } from 'zod'

const envSchema = z.object({
  NEXT_PUBLIC_SUPABASE_URL: z.string().url(),
  NEXT_PUBLIC_SUPABASE_ANON_KEY: z.string().min(1),
  SUPABASE_SERVICE_ROLE_KEY: z.string().min(1),
  DATABASE_URL: z.string().url().optional(),
  NEXT_PUBLIC_DEBUG: z.string().optional(),
})

export const env = envSchema.parse(process.env)

// Type-safe environment variables
declare global {
  namespace NodeJS {
    interface ProcessEnv extends z.infer<typeof envSchema> {}
  }
}

IDE Configuration

VS Code Setup

Essential Extensions:

// .vscode/extensions.json
{
  "recommendations": [
    "ms-vscode.vscode-typescript-next",
    "bradlc.vscode-tailwindcss",
    "esbenp.prettier-vscode",
    "ms-vscode.vscode-eslint",
    "ms-vscode.vscode-json",
    "formulahendry.auto-rename-tag",
    "christian-kohler.path-intellisense",
    "ms-vscode.vscode-todo-highlight",
    "gruntfuggly.todo-tree"
  ]
}

VS Code Settings:

// .vscode/settings.json
{
  "typescript.preferences.importModuleSpecifier": "relative",
  "editor.codeActionsOnSave": {
    "source.fixAll.eslint": true,
    "source.organizeImports": true
  },
  "editor.formatOnSave": true,
  "editor.defaultFormatter": "esbenp.prettier-vscode",
  "files.associations": {
    "*.mdx": "markdown"
  },
  "emmet.includeLanguages": {
    "javascript": "javascriptreact",
    "typescript": "typescriptreact"
  },
  "tailwindCSS.experimental.classRegex": [
    ["cva\\(([^)]*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]"],
    ["cx\\(([^)]*)\\)", "(?:'|\"|`)([^']*)(?:'|\"|`)"]
  ]
}

Debug Configuration:

// .vscode/launch.json
{
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Next.js: debug server-side",
      "type": "node",
      "request": "attach",
      "port": 9229,
      "skipFiles": ["<node_internals>/**"]
    },
    {
      "name": "Next.js: debug client-side",
      "type": "chrome",
      "request": "launch",
      "url": "http://localhost:3000"
    },
    {
      "name": "Next.js: debug full stack",
      "type": "node",
      "request": "launch",
      "program": "${workspaceFolder}/node_modules/.bin/next",
      "args": ["dev"],
      "console": "integratedTerminal",
      "skipFiles": ["<node_internals>/**"],
      "env": {
        "NODE_OPTIONS": "--inspect"
      }
    }
  ]
}

Development Tools Setup

Package.json Scripts:

{
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint",
    "lint:fix": "next lint --fix",
    "type-check": "tsc --noEmit",
    "format": "prettier --write .",
    "format:check": "prettier --check .",
    "test": "jest",
    "test:watch": "jest --watch",
    "test:coverage": "jest --coverage",
    "clean": "rm -rf .next out node_modules/.cache",
    "reset": "npm run clean && rm -rf node_modules package-lock.json && npm install",
    "analyze": "cross-env ANALYZE=true npm run build"
  }
}

Database Development Setup

Local Supabase Setup

# Install Supabase CLI
npm install -g supabase

# Initialize Supabase project
supabase init

# Start local development
supabase start

# Check status
supabase status

# Stop local instance
supabase stop

# Reset database
supabase db reset

# Generate types
supabase gen types typescript --local > types/database.ts

Supabase Configuration:

// lib/supabase.ts
import { createClient } from '@supabase/supabase-js'
import type { Database } from '@/types/database'

const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!

export const supabase = createClient<Database>(supabaseUrl, supabaseKey, {
  auth: {
    autoRefreshToken: true,
    persistSession: true,
    detectSessionInUrl: true,
  },
})

// Development-only admin client
export const supabaseAdmin = createClient<Database>(
  supabaseUrl,
  process.env.SUPABASE_SERVICE_ROLE_KEY!,
  {
    auth: {
      autoRefreshToken: false,
      persistSession: false,
    },
  }
)

Database Migrations

-- Create migration file
-- supabase/migrations/20240101000000_initial_schema.sql

-- Enable necessary extensions
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";

-- Create tables
CREATE TABLE products (
  id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
  name VARCHAR(255) NOT NULL,
  sku VARCHAR(100) UNIQUE NOT NULL,
  description TEXT,
  price DECIMAL(10,2) NOT NULL,
  is_active BOOLEAN DEFAULT true,
  user_id UUID REFERENCES auth.users(id),
  created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
  updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);

-- Create RLS policies
ALTER TABLE products ENABLE ROW LEVEL SECURITY;

CREATE POLICY "Users can view their own products" ON products
  FOR SELECT USING (auth.uid() = user_id);

CREATE POLICY "Users can insert their own products" ON products
  FOR INSERT WITH CHECK (auth.uid() = user_id);

-- Create indexes
CREATE INDEX idx_products_user_id ON products(user_id);
CREATE INDEX idx_products_sku ON products(sku);
CREATE INDEX idx_products_is_active ON products(is_active);

-- Create function to update updated_at
CREATE OR REPLACE FUNCTION update_updated_at_column()
RETURNS TRIGGER AS $$
BEGIN
  NEW.updated_at = NOW();
  RETURN NEW;
END;
$$ language 'plpgsql';

-- Create trigger
CREATE TRIGGER update_products_updated_at 
  BEFORE UPDATE ON products 
  FOR EACH ROW 
  EXECUTE FUNCTION update_updated_at_column();

Development Workflow

Git Configuration

# Configure Git
git config --global user.name "Your Name"
git config --global user.email "your.email@example.com"

# Set up Git hooks
npx husky install
npx husky add .husky/pre-commit "npm run lint && npm run type-check"
npx husky add .husky/commit-msg "npx commitlint --edit $1"

.gitignore for Next.js:

# Dependencies
/node_modules
/.pnp
.pnp.js

# Testing
/coverage

# Next.js
/.next/
/out/

# Production
/build

# Environment files
.env*.local
.env.production

# Debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# Local
.DS_Store
*.tsbuildinfo
next-env.d.ts

# Supabase
.branches
.temp

# IDE
.vscode/settings.json
.idea

Linting and Formatting

ESLint Configuration:

// eslint.config.mjs
import { dirname } from "path"
import { fileURLToPath } from "url"
import { FlatCompat } from "@eslint/eslintrc"

const __filename = fileURLToPath(import.meta.url)
const __dirname = dirname(__filename)

const compat = new FlatCompat({
  baseDirectory: __dirname,
})

const eslintConfig = [
  ...compat.extends("next/core-web-vitals", "next/typescript"),
  {
    rules: {
      "@typescript-eslint/no-unused-vars": "error",
      "@typescript-eslint/no-explicit-any": "warn",
      "prefer-const": "error",
      "no-var": "error",
    },
  },
]

export default eslintConfig

Prettier Configuration:

// prettier.config.cjs
module.exports = {
  semi: false,
  trailingComma: "es5",
  singleQuote: true,
  tabWidth: 2,
  useTabs: false,
  printWidth: 80,
  endOfLine: "lf",
  plugins: ["prettier-plugin-tailwindcss"],
}

Debugging Techniques

Browser DevTools

// Debug helpers
export const debug = {
  log: (message: string, data?: any) => {
    if (process.env.NEXT_PUBLIC_DEBUG === 'true') {
      console.log(`[DEBUG] ${message}`, data)
    }
  },
  
  table: (data: any) => {
    if (process.env.NEXT_PUBLIC_DEBUG === 'true') {
      console.table(data)
    }
  },
  
  time: (label: string) => {
    if (process.env.NEXT_PUBLIC_DEBUG === 'true') {
      console.time(label)
    }
  },
  
  timeEnd: (label: string) => {
    if (process.env.NEXT_PUBLIC_DEBUG === 'true') {
      console.timeEnd(label)
    }
  },
}

// React DevTools debugging
export function useDebugValue(value: any, formatter?: (value: any) => any) {
  if (process.env.NODE_ENV === 'development') {
    React.useDebugValue(value, formatter)
  }
}

Network Debugging

// API debugging middleware
export function debugApiCall(url: string, options?: RequestInit) {
  debug.log(`API Call: ${options?.method || 'GET'} ${url}`)
  debug.time(`API-${url}`)
  
  return fetch(url, options)
    .then(response => {
      debug.timeEnd(`API-${url}`)
      debug.log(`API Response: ${response.status} ${response.statusText}`)
      return response
    })
    .catch(error => {
      debug.timeEnd(`API-${url}`)
      debug.log(`API Error: ${error.message}`)
      throw error
    })
}

Performance Monitoring

Development Performance Tools

// Performance monitoring in development
export function usePerformanceMonitor() {
  useEffect(() => {
    if (process.env.NODE_ENV === 'development') {
      // Monitor component render times
      const observer = new PerformanceObserver((list) => {
        for (const entry of list.getEntries()) {
          debug.log(`Performance: ${entry.name} took ${entry.duration}ms`)
        }
      })
      
      observer.observe({ entryTypes: ['measure'] })
      
      return () => observer.disconnect()
    }
  }, [])
}

// Bundle size monitoring
export function analyzeBundleSize() {
  if (process.env.NODE_ENV === 'development') {
    import('webpack-bundle-analyzer').then(({ BundleAnalyzerPlugin }) => {
      // Add to webpack config for development analysis
    })
  }
}

Common Development Issues

Port Already in Use

# Find and kill process using port 3000
lsof -ti:3000 | xargs kill -9

# Or use different port
npm run dev -- -p 3001

Permission Denied

# Fix npm permissions (Mac/Linux)
sudo chown -R $(whoami) ~/.npm
sudo chown -R $(whoami) /usr/local/lib/node_modules

Module Resolution Issues

// tsconfig.json path mapping
{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["./src/*"],
      "@/components/*": ["./src/components/*"],
      "@/lib/*": ["./src/lib/*"],
      "@/types/*": ["./src/types/*"]
    }
  }
}

For advanced development environment configurations, refer to the Next.js Documentation and VS Code Documentation.