Yevhen Klymentiev
dark
light
console
darkness
y.klymentiev@gmail.com
Coding Standards|Rules, conventions, and best practices I follow and recommend to teams

Validation

Ensure data integrity and user safety by validating input and sanitizing output at every layer.

Validation protects against malformed or malicious data, while sanitization prevents injection attacks and output corruption. These practices are essential for backend APIs, forms, database writes, and rendered UI.

Validate all inputs at boundaries

Always validate user-provided data at the boundary of your application — whether it's an HTTP request, form input, or external API call. Never trust data coming from the client, and reject anything unexpected or malformed as early as possible.

Validation targets include:

  • Route params

  • Query parameters

  • Request bodies

  • Headers and cookies

JavaScript
Copied!
1// Directly using user input without checks
2const userId = req.body.id;
3db.users.findById(userId);
JavaScript
Copied!
1const schema = z.object({ id: z.string().uuid() });
2const { id } = schema.parse(req.body);

Sanitize data before use or display

Always sanitize input data before using it in a database query or displaying it to the user. This prevents injection attacks (SQL, XSS, NoSQL) and ensures that harmful data doesn't propagate.

Common sanitization steps:

  • Trim and normalize strings

  • Escape HTML when rendering

  • Enforce expected character sets or formats

JavaScript
Copied!
1import xss from "xss";
2
3const safeContent = xss(req.body.comment);
4renderHTML(safeContent);

Define validation schemas per context

Create explicit, reusable schemas for each use case instead of applying general-purpose validation logic. This makes validation predictable and easier to maintain.

JavaScript
Copied!
1// registration.schema.ts
2export const registerSchema = z.object({
3  email: z.string().email(),
4  password: z.string().min(8),
5});

With libraries like Zod or Yup, schemas can be reused for both client and server validation.

Validate nested structures

When handling nested objects or arrays (e.g., forms, batch data), validate each level of structure. Skipping nested validation may allow malformed or malicious data to bypass checks.

JavaScript
Copied!
1const commentSchema = z.object({
2  author: z.string(),
3  content: z.string(),
4});
5
6const postSchema = z.object({
7  title: z.string(),
8  comments: z.array(commentSchema),
9});

Use custom rules for domain-specific logic

Built-in types (e.g., email, uuid, min/max) aren't always enough. Define custom rules when domain-specific constraints apply — such as password complexity, username restrictions, or business invariants.

JavaScript
Copied!
1const passwordSchema = z
2  .string()
3  .min(8)
4  .refine((val) => /[A-Z]/.test(val), {
5    message: "Must include at least one uppercase letter",
6  });

Handle validation errors gracefully

Always catch and format validation errors in a developer- and user-friendly way. Use structured error messages, clear status codes (e.g., 400), and never expose stack traces or internals to the client.

JavaScript
Copied!
1// Sends full internal error object
2res.status(500).json(err);
JavaScript
Copied!
1res.status(400).json({
2  error: {
3    message: "Invalid request",
4    details: parsedError.issues,
5  },
6});

Sanitize Before Writing to the Database

Always sanitize user-generated content before persisting it — especially if it will later be rendered in HTML or used in dynamic queries. This protects against injection, ensures consistent data, and reduces cleanup needs later.

JavaScript
Copied!
1const cleanInput = input.trim().replace(/<\/?[^>]+(>|$)/g, "");
2await db.posts.insert({ content: cleanInput });

Prevent dangerous serialization

When exposing validated data (e.g. via JSON or headers), make sure to strip internal-only fields, such as passwords, tokens, roles, or implementation details. Expose only what is needed.

JavaScript
Copied!
1const { password, ...safeUser } = user;
2res.json(safeUser);

Use fallbacks, but validate them

Setting fallback/default values is helpful — but ensure that fallbacks are also valid. Never assign insecure or structurally invalid defaults just to avoid errors.

JavaScript
Copied!
const pageSize = req.query.pageSize || 1000; // Unreasonable
JavaScript
Copied!
const pageSize = Math.min(Number(req.query.pageSize) || 20, 100); // Cap at 100

Validate Enums and allowed values

Always validate string inputs against an explicit list of allowed values (enums). This prevents users from sending unsupported or unexpected values and ensures consistent behavior across your API.

JavaScript
Copied!
1const allowedRoles = z.enum(["admin", "editor", "viewer"]);
2const schema = z.object({ role: allowedRoles });

Validate ID formats

For database lookups or external identifiers, validate the format before querying (e.g., UUIDs, MongoDB ObjectIds, numeric IDs). This prevents unnecessary DB calls and protects against injection or scanning attacks.

JavaScript
Copied!
1import { isValidObjectId } from "mongoose";
2
3if (!isValidObjectId(req.params.id)) {
4  return res.status(400).json({ error: "Invalid ID format" });
5}

Validate dates and ranges

When accepting date/time inputs, ensure they are in a valid format (ISO or timestamp), fall within expected ranges (not in the past or far future), and are timezone-safe if relevant.

JavaScript
Copied!
1const schema = z.object({
2  scheduledAt: z.coerce.date().refine((d) => d > new Date(), {
3    message: "Date must be in the future",
4  }),
5});

Validate in middleware or controllers

Centralize validation in middleware (for generic routes) or controller-level functions (for more complex rules). Avoid spreading validation logic deep inside service layers or models.

JavaScript
Copied!
1// Express middleware
2function validateRequest(schema) {
3  return (req, res, next) => {
4    try {
5      req.body = schema.parse(req.body);
6      next();
7    } catch (err) {
8      res.status(400).json({ error: err });
9    }
10  };
11}
Styleguide: Validation | Yevhen Klymentiev