Import Organization
Quick Reference
- When to use: Organizing imports in any TypeScript file
- Enforcement: Required - ESLint
local-rules/import-order+ dprint sort - Impact if violated: Medium - confusion and merge conflicts
The Rule
Module System
Use ES modules (import/export) exclusively. Never use CommonJS (require).
Mantle is ESM-only. All packages output ESM. All Lambda bundles are ESM (.mjs).
Import Order (Strict)
Imports must follow this exact order with no blank lines between groups:
- Node built-in modules (
node:crypto,node:path) - Third-party library imports (npm packages:
aws-lambda,@middy/core) @mantleframework/*package imports (@mantleframework/env,@mantleframework/core,@mantleframework/database)@mantleframework/database/ormimports (Drizzle re-exports:eq,and,sql)- Type imports (
import type {...}) - Local/relative imports (
./utils,../shared)
IMPORTANT: No blank lines between import statements. Keep all imports as a single contiguous block.
Import Style
- Destructure imports when possible
- Group related imports from same module
- Sort alphabetically within each group (dprint handles this with
sortNamedImports: "caseInsensitive") - Use
import typefor TypeScript-only imports
Examples
Correct - Lambda Function
typescript
import type {APIGatewayProxyResult, Context} from 'aws-lambda'
import {getRequiredEnv} from '@mantleframework/env'
import {getDrizzleClient, withQueryMetrics} from '@mantleframework/database'
import {eq, and} from '@mantleframework/database/orm'
import {AppError} from '@mantleframework/errors'
import {logger} from '@mantleframework/observability'
import type {UserProfile} from '../entities/schema'
import {UserQueries} from '../entities/queries/userQueries'Correct - Package Source
typescript
import {randomUUID} from 'node:crypto'
import {Tracer} from '@aws-lambda-powertools/tracer'
import {getRequiredEnv} from '@mantleframework/env'
import type {TracerConfig} from './types'Incorrect
typescript
// Wrong - blank lines between imports
import {getRequiredEnv} from '@mantleframework/env'
import {logger} from '@mantleframework/observability'
// Wrong - CommonJS syntax
const {readFile} = require('fs/promises')
// Wrong - Direct third-party imports in handlers (violates vendor encapsulation)
import {DynamoDBClient} from '@aws-sdk/client-dynamodb'
import {drizzle} from 'drizzle-orm/postgres-js'
// Wrong - Mixed import and import type
import {AppError, type ErrorCode} from '@mantleframework/errors'
// Should separate: import {AppError} and import type {ErrorCode}@mantleframework/* Import Convention
The Mantle framework has specific import conventions for database access:
| Import From | What | Example |
|---|---|---|
@mantleframework/database | Framework API | getDrizzleClient, withQueryMetrics, RequiresTable, DatabaseOperation |
@mantleframework/database/orm | Drizzle re-exports (operators, types) | eq, and, sql, SelectModel, InsertModel |
drizzle-orm/pg-core | Schema definitions only | pgTable, column types (text, uuid, timestamp) |
typescript
// Correct - database imports follow the convention
import {getDrizzleClient, withQueryMetrics, RequiresTable} from '@mantleframework/database'
import {eq, and, sql} from '@mantleframework/database/orm'
import type {InferSelectModel} from '@mantleframework/database/orm'
import {pgTable, text, uuid, timestamp} from 'drizzle-orm/pg-core'Import Styles
Destructured Imports (Preferred)
typescript
// Good - destructured, specific
import {getRequiredEnv, getOptionalEnv} from '@mantleframework/env'
import {AppError, ValidationError} from '@mantleframework/errors'Type Imports
typescript
// Good - explicit type imports
import type {APIGatewayProxyResult} from 'aws-lambda'
import type {SelectModel, InsertModel} from '@mantleframework/database/orm'Namespace Imports (Avoid)
typescript
// Avoid - prevents tree shaking
import * as env from '@mantleframework/env'
// Prefer - specific imports
import {getRequiredEnv} from '@mantleframework/env'ES Modules vs CommonJS
Always Use ES Modules
typescript
// ESM (correct)
import {readFile} from 'node:fs/promises'
export const myFunction = () => {}
export default MyClass
// CommonJS (never)
const {readFile} = require('fs/promises')
module.exports.myFunction = () => {}Why ES Modules?
- Static analysis - Tools analyze imports at build time
- Tree shaking - Unused exports eliminated by esbuild
- Type safety - Better TypeScript integration
- Standard - ES modules are the JavaScript standard
- Node 24 - Full ESM support without flags
Enforcement
Automated
- ESLint
local-rules/import-order- Validates import ordering - dprint
importDeclaration.sortNamedImports: "caseInsensitive"- Sorts named imports alphabetically - dprint
module.sortImportDeclarations: "maintain"- Preserves manual group ordering
Code Review Checklist
- [ ] ES modules syntax used (
import/export) - [ ] Imports follow strict order
- [ ] NO blank lines between import statements
- [ ] Destructured where possible
- [ ] Type imports use
import type - [ ] No direct AWS SDK or Drizzle imports in handlers
- [ ]
@mantleframework/database/ormused for Drizzle operators - [ ] No circular dependencies
Related Patterns
- Naming Conventions - File and module naming
- Vendor Encapsulation Policy - Why AWS SDK / Drizzle imports are wrapped
- Code Formatting - dprint import formatting settings
Consistent import organization reduces cognitive load and makes dependencies clear. Follow this pattern strictly for maintainable code.