← Back to Blog

Add Dependency Error Checking to Your TypeScript CI in 2 Minutes

By Nark Team

Add dependency error checking to your TypeScript CI in two minutes. One npm ci step, no configuration required. Nark reads your tsconfig.json and checks every axios, Prisma, Stripe, and other npm package call for correct error handling on every pull request.

Quick Answer: Add this to your GitHub Actions workflow: npx nark --tsconfig tsconfig.json. That's it. No config file, no signup. Nark exits 0 (clean) or 1 (violations found) and integrates into any CI pipeline that fails on non-zero exit codes.


Step 1: Run It Locally First

Before adding to CI, see what Nark finds on your codebase:

npx nark --tsconfig ./tsconfig.json

Output looks like this:

src/services/api.ts:34 — axios
  [ERROR] axios.get() called without try-catch
  axios throws AxiosError on non-2xx responses and network failures.

src/db/users.ts:67 — @prisma/client
  [ERROR] prisma.user.create() — P2002 unique constraint not handled
  PrismaClientKnownRequestError with code P2002 is thrown on duplicate
  unique field values (e.g., duplicate email address).

src/lib/cache.ts:12 — ioredis
  [ERROR] Redis client missing .on('error') handler
  An unhandled 'error' event on an EventEmitter crashes the Node.js process.

3 violations found (3 errors, 0 warnings)
Exit code: 1

If Nark finds violations, either fix them first (recommended) or start with --min-severity warning to make existing violations non-blocking while you work through them.


Step 2: Add to GitHub Actions

# Add this step to your existing .github/workflows/ci.yml

- name: nark — dependency error handling
  run: npx nark --tsconfig tsconfig.json

That's the complete addition. Full example workflow:

name: CI

on:
  pull_request:
    branches: [main]

jobs:
  ci:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - name: TypeScript
        run: npx tsc --noEmit

      - name: ESLint
        run: npx eslint src --ext .ts,.tsx

      - name: Tests
        run: npx vitest run

      # Add this:
      - name: nark — dependency error handling
        run: npx nark --tsconfig tsconfig.json

Common Configurations

Only fail CI on errors (not warnings)

Useful when introducing Nark to an existing codebase with accumulated issues. Errors block CI; warnings are reported but don't fail the build.

- name: nark
  run: npx nark --tsconfig tsconfig.json --min-severity error

Monorepo with multiple packages

- name: nark — API
  run: npx nark --tsconfig apps/api/tsconfig.json

- name: nark — Web
  run: npx nark --tsconfig apps/web/tsconfig.json

Or using a matrix:

strategy:
  matrix:
    package: [apps/api, apps/web, packages/shared]

steps:
  # ... checkout, setup, install ...

  - name: nark — ${{ matrix.package }}
    run: npx nark --tsconfig ${{ matrix.package }}/tsconfig.json

JSON output for downstream processing

- name: nark — generate report
  run: npx nark --tsconfig tsconfig.json --format json > nark-report.json
  continue-on-error: true

- name: Upload nark report
  uses: actions/upload-artifact@v4
  with:
    name: nark-violations
    path: nark-report.json

Run in parallel with other checks

jobs:
  typecheck:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: 20, cache: 'npm' }
      - run: npm ci && npx tsc --noEmit

  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: 20, cache: 'npm' }
      - run: npm ci && npx eslint src --ext .ts,.tsx

  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: 20, cache: 'npm' }
      - run: npm ci && npx vitest run

  nark:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: 20, cache: 'npm' }
      - run: npm ci && npx nark --tsconfig tsconfig.json

Other CI Systems

CircleCI

# .circleci/config.yml
version: 2.1

jobs:
  nark:
    docker:
      - image: node:20
    steps:
      - checkout
      - run: npm ci
      - run: npx nark --tsconfig tsconfig.json

workflows:
  ci:
    jobs:
      - nark

GitLab CI

# .gitlab-ci.yml
nark:
  image: node:20
  script:
    - npm ci
    - npx nark --tsconfig tsconfig.json

Bitbucket Pipelines

# bitbucket-pipelines.yml
pipelines:
  pull-requests:
    '**':
      - step:
          name: nark — dependency error handling
          image: node:20
          script:
            - npm ci
            - npx nark --tsconfig tsconfig.json

Pre-commit hook (optional)

Run Nark before every commit locally:

# .husky/pre-commit
npx nark --tsconfig tsconfig.json --min-severity error

What Nark Checks

Nark has profiles for 160+ npm packages. The most common violations found in TypeScript SaaS projects:

PackageWhat Nark catches
axiosMissing try-catch, error.response null check on network errors
@prisma/clientP2002 (unique constraint), P2025 (not found), connection errors
stripeStripeCardError (declined card), StripeRateLimitError, auth errors
ioredisMissing .on('error') handler (causes process crash)
pgConnection errors, query errors without client release
@anthropic-ai/sdkRateLimitError, APIConnectionError
openaiRateLimitError, AuthenticationError
nodemailerSMTP errors, connection failures
@sendgrid/mailAPI errors, rate limits
jsonwebtokenTokenExpiredError, JsonWebTokenError

Fixing Common Violations

axios without try-catch

// Before (nark violation)
const response = await axios.get('/api/users');

// After (nark passes)
try {
  const response = await axios.get('/api/users');
  return response.data;
} catch (error: unknown) {
  if (axios.isAxiosError(error)) {
    if (error.response) {
      throw new ApiError(error.response.status);
    }
    throw new NetworkError(error.code ?? 'UNKNOWN');
  }
  throw error;
}

Prisma without P2002 handling

// Before (nark violation)
await prisma.user.create({ data: { email, password } });

// After (nark passes)
try {
  return await prisma.user.create({ data: { email, password } });
} catch (error) {
  if (error instanceof Prisma.PrismaClientKnownRequestError) {
    if (error.code === 'P2002') {
      throw new ConflictError('Email already in use');
    }
  }
  throw error;
}

Redis without error handler

// Before (nark violation — crashes process on connection failure)
const redis = new Redis({ host: 'localhost', port: 6379 });

// After (nark passes)
const redis = new Redis({ host: 'localhost', port: 6379 });
redis.on('error', (error) => {
  console.error('Redis connection error:', error);
});

Frequently Asked Questions

Does Nark require any configuration?

No. Nark reads your tsconfig.json and automatically discovers your TypeScript source files. It only reports on packages you actually import.

How long does Nark take to run?

15-30 seconds on a typical TypeScript project. Running it in parallel with Vitest keeps total CI time unchanged.

Will Nark have false positives?

Nark's profiles are binary checks based on documented package behavior. If a violation is reported, the specific postcondition it's checking is documented in the package's official materials. That said, if your codebase has framework-level error handling that Nark can't see (e.g., an Express error middleware), use --min-severity error or exclusion patterns.

Does Nark require a paid plan?

No. npx nark runs entirely locally. No signup, no API key, no data sent anywhere.


Try It Now

npx nark --tsconfig ./tsconfig.json

Then add to CI:

- name: nark — dependency error handling
  run: npx nark --tsconfig tsconfig.json

Nark checks 160+ packages — including axios, prisma, stripe, and redis — for correct error handling. Two minutes to add, prevents the class of production crashes that TypeScript types and ESLint can't catch.