Set up a fast, reproducible local development workflow for Snowflake with separate dev warehouses, mocked tests, and SnowSQL for rapid iteration.
snowflake-install-auth setupDEV_WH_XS) with auto-suspend-- Run once to set up isolated dev environment
CREATE WAREHOUSE IF NOT EXISTS DEV_WH_XS
WAREHOUSE_SIZE = 'XSMALL'
AUTO_SUSPEND = 60
AUTO_RESUME = TRUE
INITIALLY_SUSPENDED = TRUE;
CREATE DATABASE IF NOT EXISTS DEV_DB;
CREATE SCHEMA IF NOT EXISTS DEV_DB.SANDBOX;
-- Grant to dev role
GRANT USAGE ON WAREHOUSE DEV_WH_XS TO ROLE DEV_ROLE;
GRANT ALL ON DATABASE DEV_DB TO ROLE DEV_ROLE;
my-snowflake-project/
├── src/
│ ├── snowflake/
│ │ ├── connection.ts # Connection wrapper with connectAsync
│ │ ├── queries.ts # Typed query functions
│ │ └── types.ts # Row type definitions
│ └── index.ts
├── tests/
│ ├── unit/
│ │ └── queries.test.ts # Mocked — no Snowflake needed
│ └── integration/
│ └── snowflake.test.ts # Requires SNOWFLAKE_* env vars
├── sql/
│ ├── migrations/ # Versioned DDL scripts
│ │ ├── V001__create_users.sql
│ │ └── V002__add_orders.sql
│ └── seeds/
│ └── dev-data.sql # Sample data for dev
├── .env.local # Local secrets (git-ignored)
├── .env.example # Template for team
└── package.json
// src/snowflake/connection.ts
import snowflake from 'snowflake-sdk';
// Enable promise-based API
snowflake.configure({ logLevel: 'WARN' });
export function createSnowflakeConnection() {
return snowflake.createConnection({
account: process.env.SNOWFLAKE_ACCOUNT!,
username: process.env.SNOWFLAKE_USER!,
password: process.env.SNOWFLAKE_PASSWORD!,
warehouse: process.env.SNOWFLAKE_WAREHOUSE || 'DEV_WH_XS',
database: process.env.SNOWFLAKE_DATABASE || 'DEV_DB',
schema: process.env.SNOWFLAKE_SCHEMA || 'SANDBOX',
role: process.env.SNOWFLAKE_ROLE || 'DEV_ROLE',
});
}
// Promise wrapper for connection.execute
export function executeQuery(
conn: snowflake.Connection,
sqlText: string,
binds?: any[]
): Promise<any[]> {
return new Promise((resolve, reject) => {
conn.execute({
sqlText,
binds,
complete: (err, stmt, rows) => {
if (err) reject(err);
else resolve(rows || []);
},
});
});
}
// Promise wrapper for connect
export function connectAsync(
conn: snowflake.Connection
): Promise<snowflake.Connection> {
return new Promise((resolve, reject) => {
conn.connect((err, conn) => {
if (err) reject(err);
else resolve(conn);
});
});
}
// tests/unit/queries.test.ts
import { describe, it, expect, vi, beforeEach } from 'vitest';
// Mock the snowflake-sdk module
vi.mock('snowflake-sdk', () => ({
default: {
configure: vi.fn(),
createConnection: vi.fn(() => ({
connect: vi.fn((cb) => cb(null, { getId: () => 'mock-id' })),
execute: vi.fn(({ sqlText, complete }) => {
// Return mock data based on query
if (sqlText.includes('CURRENT_WAREHOUSE')) {
complete(null, {}, [{ WAREHOUSE: 'DEV_WH_XS' }]);
} else if (sqlText.includes('SELECT')) {
complete(null, {}, [
{ ID: 1, NAME: 'Alice' },
{ ID: 2, NAME: 'Bob' },
]);
} else {
complete(null, { getNumUpdatedRows: () => 1 }, []);
}
}),
destroy: vi.fn((cb) => cb(null)),
})),
},
}));
import { createSnowflakeConnection, executeQuery, connectAsync } from '../../src/snowflake/connection';
describe('Snowflake Queries', () => {
it('should connect and execute a query', async () => {
const conn = createSnowflakeConnection();
await connectAsync(conn);
const rows = await executeQuery(conn, 'SELECT * FROM USERS');
expect(rows).toHaveLength(2);
expect(rows[0].NAME).toBe('Alice');
});
});
// tests/integration/snowflake.test.ts
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
import { createSnowflakeConnection, connectAsync, executeQuery } from '../../src/snowflake/connection';
describe.skipIf(!process.env.SNOWFLAKE_ACCOUNT)('Snowflake Integration', () => {
let conn: any;
beforeAll(async () => {
conn = createSnowflakeConnection();
await connectAsync(conn);
// Create temp table for test isolation
await executeQuery(conn, `
CREATE TEMPORARY TABLE test_users (
id INTEGER AUTOINCREMENT, name VARCHAR(100)
)
`);
});
afterAll(async () => {
conn?.destroy(() => {});
});
it('should insert and query data', async () => {
await executeQuery(conn,
'INSERT INTO test_users (name) VALUES (?)', ['TestUser']
);
const rows = await executeQuery(conn, 'SELECT * FROM test_users');
expect(rows.length).toBeGreaterThan(0);
expect(rows[0].NAME).toBe('TestUser');
});
});
# Install SnowSQL CLI
brew install --cask snowflake-snowsql # macOS
# Configure named connection
cat >> ~/.snowsql/config << 'EOF'
[connections.dev]
accountname = myorg-myaccount
username = my_user
dbname = DEV_DB
schemaname = SANDBOX
warehousename = DEV_WH_XS
rolename = DEV_ROLE
EOF
# Quick queries
snowsql -c dev -q "SELECT COUNT(*) FROM my_table"
# Run migration scripts
snowsql -c dev -f sql/migrations/V001__create_users.sql
| Error | Cause | Solution |
|---|---|---|
000606: No active warehouse |
Dev warehouse suspended | Set AUTO_RESUME = TRUE on warehouse |
Module not found: snowflake-sdk |
Not installed | Run npm install snowflake-sdk |
Tests timeout |
Warehouse resuming from suspend | Increase test timeout to 30s, or pre-warm |
002003: Object does not exist |
Wrong database/schema context | Check .env.local DB and SCHEMA values |
See snowflake-sdk-patterns for production-ready code patterns.