Skip to content

JavaScript File Structure

Every .js file in this codebase follows the same top-to-bottom layout without exception. The order is a contract — any engineer can open any file and know exactly where to look for imports, exports, and logic.


The Order (1–12)

#SectionNotes
1Header commentWhat this file is for and what its methods do
2'use strict'Always present, always second
3ENV variablesDestructured from process.env
4Built-in Node modulesfs, path, crypto, etc.
5Third-party moduleslodash, joi, moment-timezone, etc.
6ServicesFrom services/ — including the queue service (services/queue.js)
7HelpersFrom helpers/, constants, and the feature's own helper.js
8Modelsconst models = require('.../models')
9QueuesQueue instances via queue.get('XQueue') — right after models; the service itself is up in step 6
10Module-level constantsOptional; computed from imports above
11module.exportsDeclared before the method definitions (functions are hoisted)
12Method definitionsThe actual implementations

Import Ordering Within Each Group

Within every import section, order by increasing line length. Put plain whole-module requires before destructured requires, each sub-group also ordered by increasing length.

javascript
// ✅ correct
const _ = require('lodash');
const joi = require('joi');
const moment = require('moment-timezone');

const lang = require('../../../services/language');
const queue = require('../../../services/queue');
const { ERROR_CODES, errorResponse, joiErrorsMessage } = require('../../../services/error');

// ❌ wrong — destructured before plain, and not length-ordered
const { ERROR_CODES, errorResponse } = require('../../../services/error');
const queue = require('../../../services/queue');
const _ = require('lodash');

module.exports Pattern

module.exports is declared before the function definitions. This works because function declarations are hoisted. The result is a readable summary of what the file exports, right before the implementations.

javascript
module.exports = {
  V1Login,
  V1Logout
};

async function V1Login(req, res) { ... } // END V1Login
async function V1Logout(req, res) { ... } // END V1Logout

For files exporting a single function:

javascript
module.exports = {
  requestId
};

function requestId(req, res, next) { ... } // END requestId

Function End Comment

Close every named function with // END functionName. This makes it easy to find the end of long functions and scan a file quickly.

javascript
async function V1Login(req, res) {
  // ...
} // END V1Login

Applies to all functions: actions, tasks, helpers, services, middleware.


JSDoc: Action Header

Every action file opens with a JSDoc block describing what the action does, its route, auth requirements, accepted args, success response, and all possible error codes. Every @error listed here must have a corresponding test.

javascript
/**
 * Log in a user and return an access token.
 *
 * POST /v1/users/login
 *
 * Must be logged out
 *
 * req.args = {
 *   email    - (STRING - REQUIRED): The email address of the user
 *   password - (STRING - REQUIRED): The unhashed password of the user
 * }
 *
 * Success: Return logged-in user and access token.
 * Errors:
 *   400: Login failed. Incorrect email and/or password.
 *   400: Your account is inactive, cannot log in.
 *   401: Please confirm your email to log in.
 */

Fields in req.args:

  • Type is ALL CAPS: STRING, NUMBER, BOOLEAN, OBJECT, ARRAY
  • Mark every field REQUIRED or OPTIONAL
  • Add [DEFAULT - <value>] when a default exists

Fields in req.params (URL segments):

javascript
 * req.params = {
 *   id - (STRING - REQUIRED): The UUID of the user
 * }

JSDoc: General Helper/Method Header

javascript
/**
 * Hash a plain-text token using SHA-256.
 *
 * @token  - (STRING - REQUIRED): The raw token to hash
 *
 * return: string
 */

Complete Annotated Action File

The example below demonstrates every rule: section order, import ordering within groups, module.exports before methods, function end comments, and the action JSDoc header.

javascript
/**
 * V1Login
 * Log in a user with email + password. Returns the user and a signed access token.
 */

'use strict';

// env
const { TOKEN_SECRET, NODE_ENV } = process.env;

// third-party
const joi = require('joi');
const moment = require('moment-timezone');

// services
const lang = require('../../../services/language');
const queue = require('../../../services/queue');
const { ERROR_CODES, errorResponse, joiErrorsMessage } = require('../../../services/error');

// helpers
const { ROLES } = require('../../../helpers/constants');
const { hashToken } = require('../../../helpers/logic');

// models
const models = require('../../../models');

// queues  (instances — queue service is already required above)
const AuthQueue = queue.get('AuthQueue');

// module-level constants
const MAX_LOGIN_ATTEMPTS = 5;

module.exports = {
  V1Login
};

/**
 * Log in a user and return an access token.
 *
 * POST /v1/users/login
 *
 * Must be logged out
 *
 * req.args = {
 *   email    - (STRING - REQUIRED): The email address of the user
 *   password - (STRING - REQUIRED): The unhashed password of the user
 * }
 *
 * Success: Return logged-in user and access token.
 * Errors:
 *   400: Login failed. Incorrect email and/or password.
 *   400: Your account is inactive, cannot log in.
 *   401: Please confirm your email to log in.
 */
async function V1Login(req, res) {
  const schema = joi.object({
    email:    joi.string().email().required(),
    password: joi.string().min(8).required()
  });

  const { error, value } = schema.validate(req.args);
  if (error) return errorResponse(req, ERROR_CODES.BAD_REQUEST_INVALID_ARGUMENTS, joiErrorsMessage(error));

  const { email, password } = value;

  // find user
  const user = await models.user.scope(null).findOne({ where: { email } });
  if (!user) return errorResponse(req, ERROR_CODES.USER_BAD_REQUEST_INVALID_LOGIN_CREDENTIALS);

  // validate password
  const valid = await models.user.validatePassword(password, user.password);
  if (!valid) return errorResponse(req, ERROR_CODES.USER_BAD_REQUEST_INVALID_LOGIN_CREDENTIALS);

  // check account status
  if (!user.isActive) return errorResponse(req, ERROR_CODES.USER_BAD_REQUEST_ACCOUNT_INACTIVE);
  if (!user.isEmailConfirmed) return errorResponse(req, ERROR_CODES.USER_UNAUTHORIZED_EMAIL_NOT_CONFIRMED);

  // issue token (implementation detail omitted for brevity)
  const token = 'signed-jwt-here';

  return {
    status: 200,
    success: true,
    token,
    user: user.toJSON()
  };
} // END V1Login

Quick Reference

1.  header comment
2.  'use strict'
3.  env vars
4.  built-ins
5.  third-party        ← plain requires first, by length; then destructured, by length
6.  services           ← queue SERVICE here (services/queue.js)
7.  helpers
8.  models
9.  queues             ← queue INSTANCES here (queue.get('XQueue'))
10. module-level consts
11. module.exports      ← BEFORE functions
12. function defs       ← each closes with // END name