Skip to content

Instantly share code, notes, and snippets.

@mikesparr
Created October 1, 2025 22:51
Show Gist options
  • Save mikesparr/ebba183e6819bc884b680a311a7829bf to your computer and use it in GitHub Desktop.
Save mikesparr/ebba183e6819bc884b680a311a7829bf to your computer and use it in GitHub Desktop.
Example NodeJS Express App Routes Specification

API Route Standardization Guide

This guide outlines the standards that should be followed to ensure consistency across all route files in the API.

Route File Structure

Each route file should follow this structure:

// 1. Imports
const express = require('express');
const someController = require('../controllers/someController');
const authenticate = require('../middleware/authenticate');
const authorize = require('../middleware/authorize');
const validateContext = require('../middleware/contextValidator');
const { validate, schemas } = require('../utils/validation');

// 2. Router Initialization
const router = express.Router();

// 3. Global Middleware (if applicable)
router.use(authenticate); // For protected routes only

// 4. Route Definitions
/**
 * @route HTTP_METHOD /path
 * @desc Description of what the endpoint does
 * @access Public|Private (with permission requirements)
 */
router.method('/path',
  // Middleware chain
  validate(schemas.someSchema, 'params'),
  validateContext(),
  authorize('permission:action'),
  controllerFunction
);

// 5. Export
module.exports = router;

Route Documentation

Every route must include JSDoc-style comments with:

/**
 * @route HTTP_METHOD /path/with/:params
 * @desc Brief description of endpoint functionality
 * @access Public|Private (requires permission:action)
 */

Middleware Chain Order

The middleware chain for protected routes should follow this order:

  1. Parameter Validation: validate(schemas.paramSchema, 'params')
  2. Context Validation: validateContext() (for org/env scoped routes)
  3. Authorization: authorize('permission:action')
  4. Request Body Validation: validate(schemas.bodySchema) (for POST/PUT/PATCH)
  5. Controller Function: controllerFunction

Example:

router.post('/:orgId/members/invite',
  validate(schemas.orgIdParam, 'params'),
  validateContext(),
  authorize('memberships:manage'),
  validate(schemas.organizationInviteRequest),
  organizationController.inviteUserToOrganization
);

Authentication Patterns

Public Routes (authRoutes.js pattern)

  • No global authentication middleware
  • Individual routes that need auth use authenticate middleware
// No router.use(authenticate)
router.post('/login', validate(schemas.loginRequest), authController.requestMagicLink);
router.post('/logout', authenticate, authController.logout);

Protected Routes (organizationRoutes.js pattern)

  • Apply authentication globally to all routes
  • Individual routes use authorization middleware
// Apply to all routes in this file
router.use(authenticate);

router.get('/organizations/:orgId',
  validate(schemas.orgIdParam, 'params'),
  validateContext(),
  authorize('organizations:read'),
  organizationController.getOrganization
);

Validation Patterns

Parameter Validation

Always validate route parameters:

validate(schemas.orgIdParam, 'params')           // Single param
validate(schemas.orgAndEnvParams, 'params')      // Multiple params
validate(schemas.publicationCredentialParams, 'params') // Complex params

Request Body Validation

Validate request bodies for data modification endpoints:

validate(schemas.organizationInviteRequest)     // POST requests
validate(schemas.userUpdateRequest)             // PATCH requests
validate(schemas.publicationCredentialCreateRequest) // Creation

Authorization Patterns

Use descriptive permission names that follow the pattern resource:action:

  • Read permissions: organizations:read, credentials:read, memberships:read
  • List permissions: environments:list
  • Management permissions: memberships:manage, credentials:manage, environments:manage

Context Validation

For organization/environment scoped routes, always include validateContext():

router.get('/organizations/:orgId/environments/:envId/some-resource',
  validate(schemas.orgAndEnvParams, 'params'),
  validateContext(), // Ensures user has access to org/env (compare JWT to path)
  authorize('resource:read'),
  controller.getResource
);

Route Grouping

Group related routes logically within the file:

  1. Core resource operations (CRUD on main resource)
  2. Sub-resource operations (nested resources)
  3. Administrative operations (special actions)

HTTP Methods and Paths

Follow RESTful conventions:

  • GET /resource - List resources
  • GET /resource/:id - Get single resource
  • POST /resource - Create resource
  • PUT/PATCH /resource/:id - Update resource
  • DELETE /resource/:id - Delete resource

For nested resources:

  • GET /organizations/:orgId/environments/:envId/publications/:pubId/credentials
  • POST /organizations/:orgId/environments/:envId/publications/:pubId/credentials

Special Endpoints

Impersonation Endpoints

router.post('/organizations/:orgId/environments/:envId/members/:membershipId/impersonate',
  validate(schemas.orgEnvMemberParams, 'params'),
  validateContext(),
  authorize('memberships:manage'),
  userController.impersonateMember
);

Client Authorization Endpoints

router.get('/organizations/:orgId/members/:membershipId/permissions',
  validate(schemas.orgIdAndMemberParams, 'params'),
  validateContext(),
  authorize('memberships:manage'),
  userController.getMemberPermissions
);

Error Handling

Error handling is managed by:

  1. Validation middleware - Catches validation errors
  2. Authorization middleware - Catches permission errors
  3. Controller asyncHandler - Catches business logic errors
  4. Global error middleware - Catches all unhandled errors

Routes should not include try/catch blocks as controllers handle this with asyncHandler.

File Naming Convention

Route files should be named as {resource}Routes.js:

  • authRoutes.js - Authentication endpoints
  • organizationRoutes.js - Organization-scoped endpoints
  • userRoutes.js - User-specific endpoints
  • adminRoutes.js - Administrative endpoints
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment