Skip to content

Patterns and Anti-Patterns

Prerequisites: Familiarity with Jaypie basics

This page documents Jaypie coding patterns to follow and anti-patterns to avoid.

import { NotFoundError, BadRequestError } from "jaypie";
if (!user) throw NotFoundError("User not found");
if (!email) throw BadRequestError("Email required");
// Bad
throw new Error("User not found");
throw new Error("Email required");
import { BadGatewayError, log } from "jaypie";
try {
await externalApi.call();
} catch (error) {
log.error("External API failed");
log.var({ error: error.message });
throw BadGatewayError();
}
// Bad - exposes stack trace
throw InternalError(error.message);
throw InternalError(`DB error: ${dbError.stack}`);
// Happy path = trace only
log.trace("[getUser] fetching");
const user = await db.findById(id);
log.trace("[getUser] found");
// Unexpected but handled = debug
if (!cached) {
log.debug("[getUser] cache miss");
}
// Problems = warn/error
log.error("Payment service down");
// Bad
log.info("Starting function");
log.info("Got user");
log.info("Returning result");
log.var({ userId: "123" });
log.var({ orderId: "456" });
// Bad
log.var({ userId: "123", orderId: "456", status: "active" });
log.trace("[processOrder] starting");
log.trace("[processOrder] validated");
log.trace("[processOrder] complete");
function createUser({ name, email, role }) {
// ...
}
function fetchUser(userId, { includeProfile } = {}) {
// Single required + options object OK
}
// Bad
function createUser(name, email, role, active, createdBy) {
// ...
}
import { BadRequestError, log, NotFoundError, uuid } from "jaypie";
const config = {
apiKey: process.env.API_KEY,
database: process.env.DATABASE,
timeout: 5000,
};
// Bad
import { log, uuid, BadRequestError, NotFoundError } from "jaypie";
const DEFAULT_TIMEOUT = 5000;
const MAX_RETRIES = 3;
const API_VERSION = "v1";
function fetchData() {
return fetch(url, { timeout: DEFAULT_TIMEOUT });
}
// Bad
function fetchData() {
return fetch(url, { timeout: 5000 });
}
describe("Function", () => {
describe("Base Cases", () => { /* ... */ });
describe("Error Conditions", () => { /* ... */ });
describe("Security", () => { /* ... */ });
describe("Observability", () => { /* ... */ });
describe("Happy Paths", () => { /* ... */ });
describe("Features", () => { /* ... */ });
describe("Specific Scenarios", () => { /* ... */ });
});
describe("Observability", () => {
it("does not log above trace on success", async () => {
await handler();
expect(log.debug).not.toHaveBeenCalled();
expect(log.warn).not.toHaveBeenCalled();
expect(log.error).not.toHaveBeenCalled();
});
});
src/
├── utils/
│ ├── helper.ts
│ └── helper.spec.ts # Sibling, not in __tests__
// Bad
src/
├── utils/
│ └── helper.ts
__tests__/
└── utils/
└── helper.spec.ts
expressHandler(
async (req) => {
return { user: req.locals.user };
},
{
setup: [
async (req) => {
req.locals.user = await loadUser(req);
},
],
}
);
// Good
expressHandler(async (req) => {
return { data: result };
});
// Bad
expressHandler(async (req, res) => {
res.json({ data: result });
});
function process(value?: string) {
if (!value) {
throw BadRequestError("Value required");
}
// Now value is string, not string | undefined
return value.toUpperCase();
}
if (isJaypieError(error)) {
return res.status(error.status).json(error.body());
}
// User asked to add validation
function createUser({ email }) {
if (!email.includes("@")) {
throw BadRequestError("Invalid email");
}
// Just add validation, don't refactor everything
}
// Bad - user just asked to add validation
// Don't create EmailValidator class
// Don't add validation framework
// Don't refactor surrounding code
// If removing a feature, delete it completely
// Don't leave commented code
// Don't add backwards-compat shims
import { isProductionEnv, isLocalEnv } from "jaypie";
if (isProductionEnv()) {
// Production behavior
}
// Bad
if (process.env.NODE_ENV === "production") {
// ...
}