We're sorry but this app doesn't work properly without JavaScript enabled. Please enable it to continue.

This lesson's interactive features are locked, please to keep using them

Data Leaks

An endpoint can pass every authorization check and still reveal more data than it should. Imagine an online bookstore that sends its full inventory records to every shopper:

// broken: exposes complete database records
app.get("/api/books", (_req: Request, res: Response) => {
  res.json({ books: listAllBooks() });
});

Those records might include supplier costs, unpublished titles, acquisition notes, and internal audit fields. The storefront didn't need any of that.

Maybe the endpoint was safe when the table it was reading from only contained public fields. But as the data model grows, every endpoint that returns the whole record inherits its new private data. Oopsie.

Explicit Response Shapes

You should explicitly control what crosses the server-to-client boundary! A typed response shape gives you a stable public contract that doesn't grow just because the database model does:

interface BookResponse {
  id: number;
  title: string;
  author: string;
  price_cents: number;
}

function toBookResponse(book: Book): BookResponse {
  return {
    id: book.id,
    title: book.title,
    author: book.author,
    price_cents: book.price_cents,
  };
}

// fixed: returns an explicit public representation
app.get("/api/books", (_req: Request, res: Response) => {
  const books = listPublishedBooks();
  res.json({ books: books.map(toBookResponse) });
});

Return the fields the client is allowed to see, not the object you happen to store in your database.

Assignment

Bearly Secure's public product and customer order APIs return complete database records. Return explicit public response shapes instead.

  1. type ProductResponse = {
      id: number;
      name: string;
      description: string;
      image_path: string;
      price_cents: number;
    };
    
    type OrderResponse = {
      id: number;
      status: Order["status"];
      total_cents: number;
      created_at: string;
    };
    
    type OrderItemResponse = {
      product_id: number;
      product_name: string;
      quantity: number;
      price_cents: number;
    };
    

    Order["status"] needs the Order type imported from ../orders/index.ts. The Product and OrderItem types come from the same modules as their query functions.

With Bearly Secure still running, run and submit the CLI tests from the project root.