Steve Kinney

Branded Types with Express

Sometimes basic types aren’t enough. For IDs, tokens, and other special strings, we can use branded types:

// Define branded types
type UserId = string & { readonly _brand: unique symbol };
type SessionToken = string & { readonly _brand: unique symbol };

// Create functions to safely create branded types
function createUserId(id: string): UserId {
  return id as UserId;
}

function createSessionToken(token: string): SessionToken {
  return token as SessionToken;
}

// Use in request and response types
interface GetUserRequest {
  params: {
    userId: UserId;
  };
}

interface UserResponse {
  id: UserId;
  username: string;
  email: string;
}

interface AuthResponse {
  token: SessionToken;
  user: UserResponse;
}

// Example usage
app.get('/users/:userId', (req: Request<GetUserRequest['params']>, res: Response<UserResponse>) => {
  const rawUserId = req.params.userId;

  // Convert string to branded type
  const userId = createUserId(rawUserId);

  // Now we have a type-safe userId that can't be confused with other string IDs
  const user = getUserById(userId);

  res.json(user);
});

app.post('/login', (req: Request, res: Response<AuthResponse>) => {
  // Generate a session token
  const token = createSessionToken(generateRandomToken());

  // Get user
  const userId = createUserId('123');
  const user = getUserById(userId);

  // Return typed response
  res.json({
    token,
    user,
  });
});

Branded types ensure that you don’t accidentally mix up different types of IDs or tokens, even though they’re all strings underneath.

Last modified on .