Setting up Pulsar in a Drizzle Project

From zero to a fully analyzed Drizzle codebase

This tutorial walks through creating a fresh Drizzle project, adding Pulsar, and fixing the violations it finds.

Create a Drizzle project

Set up a new project with Drizzle ORM and SQLite:

mkdir my-app && cd my-app
npm init -y
npm install drizzle-orm better-sqlite3
npm install -D drizzle-kit @types/better-sqlite3

mkdir src

Create a minimal schema and query file:

src/schema.ts
import { sqliteTable, integer, text } from "drizzle-orm/sqlite-core";

export const users = sqliteTable("users", {
  id: integer("id").primaryKey(),
  name: text("name"),
  email: text("email").unique(),
});

export const posts = sqliteTable("posts", {
  id: integer("id").primaryKey(),
  title: text("title"),
  authorId: integer("author_id"),
});
src/queries.ts
import { db } from "./db";
import { users, posts } from "./schema";
import { eq } from "drizzle-orm";

// --- Problematic queries ---

// 1. SELECT * (implicit)
const allUsers = db.select().from(users);

// 2. No LIMIT
const someUsers = db.select({ id: users.id }).from(users);

// 3. No WHERE on findFirst
const firstUser = db.query.users.findFirst();

// 4. Query inside a loop
const ids = [1, 2, 3];
for (const id of ids) {
  const user = db.select().from(users).where(eq(users.id, id));
}

// 5. Missing await
const user = db.select({ id: users.id }).from(users).where(eq(users.id, 1));
src/db.ts
import { drizzle } from "drizzle-orm/better-sqlite3";
import Database from "better-sqlite3";

const sqlite = new Database(":memory:");
export const db = drizzle(sqlite);

Run Pulsar for the first time

Generate a default config:

pulsar init

Open pulsar.toml and notice all 12 rules are enabled. For now we will leave it as-is.

Run the check:

pulsar check src/

You should see output similar to:

src/queries.ts:8:20  error    no-select-star       Avoid implicit SELECT *. Specify columns explicitly.

  const allUsers = db.select().from(users);
                     ^^^^^^^^^^^^^^^^^^^^^^^

src/queries.ts:11:20  warning  no-missing-limit      Query is missing a LIMIT clause.

  const someUsers = db.select({ id: users.id }).from(users);
                                            ^^^^^^^^^^^^^^^^^^^^

src/queries.ts:14:20  error    no-unbounded-find     Query is unbounded — add a .where() or .limit().

  const firstUser = db.query.users.findFirst();
                                           ^^^

src/queries.ts:18:22  warning  no-query-in-loop      Database query inside a loop.

    const user = db.select().from(users).where(eq(users.id, id));
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

src/queries.ts:22:20  error    no-missing-await      Query is missing an `await`.

  const user = db.select({ id: users.id }).from(users).where(eq(users.id, 1));
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Fix each violation

Fix 1: no-select-star

Replace db.select() with explicit column selection:

src/queries.ts
// Before
const allUsers = db.select().from(users);

// After
const allUsers = db.select({ id: users.id, name: users.name, email: users.email }).from(users);

Fix 2: no-missing-limit

Add .limit() to bounded queries:

src/queries.ts
// Before
const someUsers = db.select({ id: users.id }).from(users);

// After
const someUsers = db.select({ id: users.id }).from(users).limit(100);

Fix 3: no-unbounded-find

Add a where clause to findFirst:

src/queries.ts
// Before
const firstUser = db.query.users.findFirst();

// After
const firstUser = db.query.users.findFirst({
  where: eq(users.id, 1),
});

Fix 4: no-query-in-loop

Move the query outside the loop using inArray:

src/queries.ts
// Before
for (const id of ids) {
  const user = db.select().from(users).where(eq(users.id, id));
}

// After
import { inArray } from "drizzle-orm";

const usersBatch = await db
  .select({ id: users.id, name: users.name, email: users.email })
  .from(users)
  .where(inArray(users.id, ids))
  .limit(100);

Fix 5: no-missing-await

Add await to the query expression:

src/queries.ts
// Before
const user = db.select({ id: users.id }).from(users).where(eq(users.id, 1));

// After
const user = await db.select({ id: users.id }).from(users).where(eq(users.id, 1));

Verify the fixes

Run Pulsar again:

pulsar check src/

No output means zero violations.

Key takeaways

  • Start with pulsar init to get a baseline config
  • Fix violations one at a time — Pulsar's output pinpoints the exact location
  • The most common fixes are adding await, adding .limit(), and moving queries out of loops
  • Use pulsar explain <rule-id> to learn more about any rule

Last updated on

On this page