<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Ayomide Ajewole</title>
    <description>The latest articles on DEV Community by Ayomide Ajewole (@og_wunderkind).</description>
    <link>https://dev.to/og_wunderkind</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3428294%2F13d14d8a-ce6e-4c4a-b6fd-143a035d9ad0.png</url>
      <title>DEV Community: Ayomide Ajewole</title>
      <link>https://dev.to/og_wunderkind</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/og_wunderkind"/>
    <language>en</language>
    <item>
      <title>Understanding Subqueries in SQL and Building JSON Directly in PostgreSQL</title>
      <dc:creator>Ayomide Ajewole</dc:creator>
      <pubDate>Mon, 13 Oct 2025 15:11:57 +0000</pubDate>
      <link>https://dev.to/og_wunderkind/understanding-subqueries-in-sql-and-building-json-directly-in-postgresql-56d3</link>
      <guid>https://dev.to/og_wunderkind/understanding-subqueries-in-sql-and-building-json-directly-in-postgresql-56d3</guid>
      <description>&lt;p&gt;There's a level of obsession with the need for optimization (speed and memory) required to be a good backend engineer. One useful tool in database optimization is SQL subqueries.&lt;br&gt;
A subquery is a query inside another query. It allows you to pull related data or computed results without writing multiple queries or heavy joins. It also allows you to perform calculations dynamically and aggregate data.&lt;br&gt;
There are different kinds of subqueries based on the type of data you want to return.&lt;/p&gt;
&lt;h2&gt;
  
  
  Scalar Subqueries
&lt;/h2&gt;

&lt;p&gt;Scalar subqueries return a single computed value needed within an outer query, which is useful for comparisons or assignments.&lt;br&gt;
For example, say you have two tables:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;users
-----
id | name

posts
-----
id | title | user_id | status
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A way to get the post count for each user could be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SELECT u.*, COUNT(p.id) AS post_count
FROM users u
LEFT JOIN posts p ON p.user_id = u.id
GROUP BY u.id;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But with a scalar subquery, you can retrieve it like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SELECT 
  u.*,
  (SELECT COUNT(*) FROM posts WHERE posts.user_id = u.id) AS post_count
FROM users u;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This form is often easier to extend and can sometimes perform better, especially with proper indexes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Column Subqueries
&lt;/h2&gt;

&lt;p&gt;A column subquery returns a single column for multiple rows. This is useful when the outer query needs to compare values with multiple rows, so it is often used after the &lt;code&gt;IN&lt;/code&gt;, &lt;code&gt;ANY&lt;/code&gt;, or &lt;code&gt;EXISTS&lt;/code&gt; operator.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SELECT u.* 
FROM users u 
WHERE u.id IN (
  SELECT DISTINCT p.user_id 
  FROM posts p 
  WHERE p.status = 'active'
);

-- OR 
SELECT u.* 
FROM users u 
WHERE EXISTS (
  SELECT 1 
  FROM posts p 
  WHERE p.user_id = u.id 
  AND p.status = 'active'
);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Row Subqueries
&lt;/h2&gt;

&lt;p&gt;A row subquery returns a single row with multiple columns. This is where PostgreSQL's JSON constructors come in. With &lt;code&gt;json_build_object&lt;/code&gt;, you can build objects that contain multiple columns from another table in each row of the outer query.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SELECT
  u.id,
  u.name,
  (
    SELECT json_build_object(
      'id', p.id,
      'title', p.title,
      'status', p.status
    )
    FROM posts p
    WHERE p.user_id = u.id
    ORDER BY p.created_at DESC
    LIMIT 1
  ) AS latest_post
FROM users u;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This allows you to fetch the latest post for each user without multiple queries or joins. The result would look like this in JSON:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[
  {
    "u_id": 1,
    "u_name": "Alice",
    "latest_post": {
      "id": 12,
      "title": "Optimizing SQL Queries",
      "status": "published",
      "created_at": "2025-10-11T09:25:43.125Z"
    }
  },
  {
    "u_id": 2,
    "u_name": "Bob",
    "latest_post": {
      "id": 15,
      "title": "Working with Subqueries",
      "status": "draft",
      "created_at": "2025-10-10T14:02:19.761Z"
    }
  }
]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Very cool stuff.&lt;/p&gt;

&lt;h2&gt;
  
  
  Table Subqueries
&lt;/h2&gt;

&lt;p&gt;A table subquery returns multiple rows and columns and can be useful for aggregating, filtering, or joining computed data. It behaves like a temporary, in-memory table you can join or select from, and is powerful for modularizing complex queries. Here, we use another powerful PostgreSQL JSON constructor called &lt;code&gt;json_agg&lt;/code&gt;.&lt;br&gt;
A good example is if you want to fetch each user with their last 10 posts.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SELECT
  u.id,
  u.name,
  (
    SELECT json_agg(
      json_build_object(
        'id', p.id,
        'title', p.title,
        'status', p.status,
        'created_at', p.created_at
      )
    )
    FROM posts p
    WHERE p.user_id = u.id
    ORDER BY p.created_at DESC
    LIMIT 10
  ) AS posts
FROM users u;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;json_build_object()&lt;/code&gt; creates a JSON object for each post and &lt;code&gt;json_agg()&lt;/code&gt; aggregates all those post objects into a single JSON array. Each user gets their own array of posts, automatically ordered by &lt;code&gt;created_at DESC&lt;/code&gt;. This can outperform ORM relations and joins, especially when the tables and data are large and only specific fields are needed.&lt;br&gt;
The result would look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[
  {
    "u_id": 1,
    "u_name": "Alice",
    "posts": [
      {
        "id": 14,
        "title": "Optimizing SQL Queries",
        "status": "published",
        "created_at": "2025-10-11T09:25:43.125Z"
      },
      {
        "id": 9,
        "title": "Getting Started with PostgreSQL JSON Functions",
        "status": "published",
        "created_at": "2025-09-15T17:12:09.872Z"
      },
      //...up to 10 posts
    ]
  },
  {
    "u_id": 2,
    "u_name": "Bob",
    "posts": [
      {
        "id": 15,
        "title": "Working with Subqueries",
        "status": "draft",
        "created_at": "2025-10-10T14:02:19.761Z"
      },
      //...up to 10 posts
    ]
  }
]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is very useful when fetching data for back-office dashboards, analytics, etc.&lt;/p&gt;

&lt;h2&gt;
  
  
  Caveats
&lt;/h2&gt;

&lt;p&gt;As powerful as subqueries and JSON constructors are, there are potential mistakes that can lead to performance issues, and considerations to be made based on your application's use case.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Forgetting to Handle Null Results&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Use &lt;code&gt;COALESCE&lt;/code&gt; to handle null values.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;COALESCE (
(
    SELECT json_agg(
      json_build_object(
        'id', p.id,
        'title', p.title,
        'status', p.status,
        'created_at', p.created_at
      )
    )
    FROM posts p
    WHERE p.user_id = u.id
    ORDER BY p.created_at DESC
    LIMIT 10
  ), '[]'
) AS posts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Not Indexing Foreign Keys&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This can be disastrous, as PostgreSQL will perform a full table scan per row if your subquery references another table and the foreign key is not indexed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Building Huge JSON Objects in One Go&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you try to aggregate too many rows into one JSON array (e.g., all posts in your entire database), you can hit memory limits or experience slowdowns. Instead:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Use pagination (&lt;code&gt;LIMIT&lt;/code&gt;, &lt;code&gt;OFFSET&lt;/code&gt;) in subqueries&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Or aggregate per entity (user, order, etc.), not globally.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Subqueries vs. Joins - Structuring vs. Combining Data
&lt;/h2&gt;

&lt;p&gt;The prevalent argument against subqueries is whether it's better to join tables since PostgreSQL optimizes &lt;code&gt;JOINs&lt;/code&gt; so well. Most arguments relegate the need for subqueries to code readability and maintainability, but I believe it is always dependent on the data and your use case.&lt;/p&gt;

&lt;p&gt;One thing that always helps is to use PostgreSQL's &lt;code&gt;EXPLAIN ANALYZE&lt;/code&gt;. It is your best friend for performance debugging.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;EXPLAIN ANALYZE
SELECT ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It will show you whether your subquery is executed once per row (bad) or optimized via index scans (good), and you can ultimately see for yourself which approach is best. &lt;/p&gt;

&lt;p&gt;Joins are great for combining data across tables, for example, retrieving a list of users with their corresponding orders. They shine when you need flat, tabular data and care about speed across large datasets.&lt;/p&gt;

&lt;p&gt;Subqueries, on the other hand, are better for structuring data. They let you embed small, scoped queries inside larger ones, so each part of your query can focus on a specific task: filtering, computing, or shaping data into a nested structure, which enables you to reduce post-processing in your backend code and focus on business logic- every backend engineer's desire.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bonus  - TypeORM Example
&lt;/h2&gt;

&lt;p&gt;I love TypeORM, and since most software developers use ORMs, here are a couple of samples of what using subqueries in TypeORM would look like in relation to the earlier used queries:&lt;/p&gt;

&lt;p&gt;This gets an array of users with a computed &lt;code&gt;post_count&lt;/code&gt; in a single query.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const userRepo = dataSource.getRepository(User);

const users = await userRepo
  .createQueryBuilder('u')
  .addSelect((qb) =&amp;gt; {
    return qb
      .select('COUNT(p.id)', 'post_count')
      .from(Post, 'p')
      .where('p.user_id = u.id');
  }, 'post_count')
  .getRawMany();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This fetches all users and their posts, and merges them into a single JSON with one query.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const users = await dataSource
  .getRepository(User)
  .createQueryBuilder('u')
  .select(['u.id', 'u.name'])
  .addSelect((qb) =&amp;gt; {
    return qb
      .subQuery()
      .select(`json_agg(json_build_object('id', p.id, 'title', p.title))`)
      .from(Post, 'p')
      .where('p.user_id = u.id');
  }, 'posts')
  .getRawMany();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Summarily, by combining subqueries and PostgreSQL's JSON functions, you're letting the database handle both data retrieval and shaping, something it's really good at. Your backend no longer needs to map, merge, or format objects. It simply returns the JSON the client needs, straight from the database with zero to minimal round-trips. Again, every backend engineer's desire. &lt;/p&gt;

&lt;p&gt;Cheers.&lt;/p&gt;

</description>
      <category>sql</category>
      <category>postgres</category>
      <category>webdev</category>
      <category>typeorm</category>
    </item>
    <item>
      <title>How to Use Postman Scripts and Dynamic Variables for Faster Testing</title>
      <dc:creator>Ayomide Ajewole</dc:creator>
      <pubDate>Sat, 04 Oct 2025 11:14:25 +0000</pubDate>
      <link>https://dev.to/og_wunderkind/how-to-use-postman-scripts-and-dynamic-variables-for-faster-testing-25lc</link>
      <guid>https://dev.to/og_wunderkind/how-to-use-postman-scripts-and-dynamic-variables-for-faster-testing-25lc</guid>
      <description>&lt;p&gt;If you use Postman to test your APIs, you’re already saving time, but there’s an easy way to make it even more powerful. With a few lightweight scripts, you can automate repetitive tasks, chain requests together, and focus more on building.&lt;br&gt;
In this article, you'll see how to supercharge your API testing with Postman's lightweight scripting environment.&lt;br&gt;
It all lies in the scripts tab of your request.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyo38o9grxfm8ngd7oid0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyo38o9grxfm8ngd7oid0.png" alt="The scripts tab in a postman request" width="800" height="310"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Environment Variables
&lt;/h2&gt;

&lt;p&gt;Just like in code, you can use environment variables within each Postman collection to hold values that are frequently used or used in multiple requests.&lt;br&gt;
Common use cases include base URLs, IDs, and auth tokens/JWTs. So, instead of using full endpoints for each request, you can use &lt;code&gt;base_url&lt;/code&gt; like in the image above. This is especially useful if you run a microservice architecture with different base URLs for different services. It is also very useful if you often need to test with multiple user IDs or auth tokens. Realistically, your collection could then look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6172dkmhjze0y5dvds65.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6172dkmhjze0y5dvds65.png" alt="Environment Variables Tab in Postman" width="800" height="551"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To create a new environment, click on the dropdown highlighted in black in the image, and to view the variables in your environment, click on the icon highlighted in red.&lt;/p&gt;

&lt;p&gt;You can mark environment variables as sensitive, so the values are masked and your teammates know to be careful with them. You can also share variable values with your teammates in real time.&lt;/p&gt;

&lt;p&gt;Now, let's talk about scripts.&lt;/p&gt;

&lt;p&gt;Postman provides a lightweight environment that allows you to modify, automate, and transform your requests and responses using JavaScript. The scripts can either be Pre-request or Post-response.&lt;/p&gt;
&lt;h2&gt;
  
  
  Pre-request Scripts
&lt;/h2&gt;

&lt;p&gt;Pre-request scripts are JavaScript (or TypeScript-like) snippets that execute before your request goes out. &lt;br&gt;
Example: Signing Requests (HMAC or SHA256)&lt;br&gt;
Some APIs require you to sign your requests with a secret key. Doing this manually is a pain, but a pre-request script makes it easy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Pre-request script for signing request body
const crypto = require('crypto-js');

const body = pm.request.body.raw;
const secret = pm.environment.get("API_SECRET");

const signature = crypto.HmacSHA256(body, secret).toString();
pm.environment.set("signature", signature); // Do this if you have the signed secret key as a separate environment variable

// Add signature header dynamically
pm.request.headers.add({
  key: "X-Signature",
  value: signature
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, each outgoing request is securely signed without any manual step.&lt;/p&gt;

&lt;h2&gt;
  
  
  Post-response Scripts
&lt;/h2&gt;

&lt;p&gt;Post-response scripts allow you to validate responses, store values for future requests, and even automate multi-step workflows.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Test script
pm.test("Login successful", () =&amp;gt; {
  pm.response.to.have.status(200);
});

// Sets an environment variable with the token and user's ID from the login response
const response = pm.response.json();
pm.environment.set("USER_1_AUTH_TOKEN", response.token);
pm.environment.set("USER_1_ID", response.id);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, the token and user ID are automatically stored in your environment and ready to use in any future request.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bonus
&lt;/h2&gt;

&lt;p&gt;Postman provides auto-generated dynamic variables that you can drop right into your request body without writing any script. Here are a few examples:&lt;br&gt;
&lt;code&gt;{{$guid}}&lt;/code&gt; → Generates a random UUID&lt;br&gt;
&lt;code&gt;{{$timestamp}}&lt;/code&gt; → Current UNIX timestamp&lt;br&gt;
&lt;code&gt;{{$randomInt}}&lt;/code&gt; → Random integer&lt;br&gt;
&lt;code&gt;{{$randomEmail}}&lt;/code&gt; → Random email address&lt;br&gt;
&lt;code&gt;{{$randomPassword}}&lt;/code&gt; → Random email address&lt;br&gt;
&lt;code&gt;{{$randomUsername}}&lt;/code&gt; → Random email address&lt;br&gt;
&lt;code&gt;{{$isoTimestamp}}&lt;/code&gt; → ISO timestamp&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "username": "{{$randomUsername}}",
  "timestamp": "{{$isoTimestamp}}",
  "email": "{{$randomEmail}}",
  "randomPassword": "{{$randomPassword}}"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These are especially useful when you need mock data for testing your endpoints, and there are so many options. You can find them out &lt;a href="https://learning.postman.com/docs/tests-and-scripts/write-scripts/variables-list/" rel="noopener noreferrer"&gt;here&lt;/a&gt;, or simply start typing &lt;code&gt;{{$&lt;/code&gt; in a request body value to see the options.&lt;br&gt;
So, Postman offers a wide range of tools to make API testing easier, and I hope I've been able to help you see some, and that you're encouraged to explore even more to make your testing workflow easier. &lt;br&gt;
Cheers.&lt;/p&gt;

</description>
      <category>postman</category>
      <category>softwaredevelopment</category>
      <category>api</category>
      <category>javascript</category>
    </item>
    <item>
      <title>How to Make Your Mongoose Models Smarter and Your Code Cleaner with Setters, Getters, and More</title>
      <dc:creator>Ayomide Ajewole</dc:creator>
      <pubDate>Fri, 26 Sep 2025 08:46:16 +0000</pubDate>
      <link>https://dev.to/og_wunderkind/how-to-make-your-mongoose-models-smarter-and-your-code-cleaner-with-setters-getters-and-more-22ag</link>
      <guid>https://dev.to/og_wunderkind/how-to-make-your-mongoose-models-smarter-and-your-code-cleaner-with-setters-getters-and-more-22ag</guid>
      <description>&lt;p&gt;I've been building, deploying, and maintaining Node.js applications for years now, and my ODM of choice, like for most others, when using MongoDB, the most popular non-relational database in the Node ecosystem, is &lt;a href="https://mongoosejs.com/" rel="noopener noreferrer"&gt;Mongoose&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I'm pretty sure you know what Mongoose is if you're reading this. Still, in case you don't, it is an object data modelling tool that provides a straightforward, schema-based approach to model application data with validation, query building, and more out of the box. All of this is useful because it can be a hassle to deal with MongoDB's native Node.js drivers, query language, and schema-less structure, which makes data consistency a nightmare. In the words of Mongoose's creators:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Let's face it, writing MongoDB validation, casting and business logic boilerplate is a drag. That's why we wrote Mongoose.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So Mongoose makes it very easy to work with MongoDB, but many developers miss just how feature-packed it is. Beyond simple schema definitions and queries, Mongoose has a toolbox of powerful features that can dramatically improve how you structure, validate, and maintain your codebase.&lt;/p&gt;

&lt;p&gt;In this post, we'll go beyond the basics - diving into getters, setters, virtuals, custom validators, middleware, statics, and methods - so you can start writing cleaner and smarter models.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getters and Setters
&lt;/h2&gt;

&lt;p&gt;Every developer has encountered use cases that require data to be stored separately from its usage in code. A common use case is the standard practice of storing names and emails in lowercase for consistency, but displaying them in title case.&lt;br&gt;
With getters and setters, you can handle that directly at the schema level:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const transactionSchema = new mongoose.schema({
  email: {
    type: String,
    set: v =&amp;gt; v.toLowerCase(),
  },
  firstName: {
    type: String,
    set: v =&amp;gt; v.toLowerCase(),
    get: v =&amp;gt; toTitleCase(v), // assuming you have a toTitleCase util
  },
  lastName: {
    type: String,
    set: v =&amp;gt; v.toLowerCase(),
    get: v =&amp;gt; toTitleCase(v),
  },
})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The get and set methods are functions that can be used to modify the variable as desired before it gets fetched from or saved to the DB, similar to transformers in TypeORM.&lt;/p&gt;

&lt;h2&gt;
  
  
  Virtuals
&lt;/h2&gt;

&lt;p&gt;Let's say the product team suddenly wants to display a user's full name on the frontend. Instead of concatenating names manually everywhere in your code, you can define a virtual property. It lets you define computed properties that don't actually exist in the database.&lt;br&gt;
Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;userSchema.virtual('fullName').get(function () {
  return `${this.firstName} ${this.lastName}`;
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will allow the fullName field to get returned whenever the user collection is queried, even though it's not stored in the database.&lt;/p&gt;

&lt;h2&gt;
  
  
  Middleware(Hooks)
&lt;/h2&gt;

&lt;p&gt;Think of it just like a regular middleware. A logic you want to execute before you go on to the main logic, the model event in this case, like save, find, remove, and more. Mongoose provides built-in event-based middleware. It can be used for complex validation, removing dependent documents, triggers, and more.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;userSchema.pre('save', function (next) {
  if (this.phoneNumber) {
  this.phoneNumber = formatPhoneNumber(this.phoneNumber); // format phone number to desired form.
}
  next();
});
userSchema.pre('remove', async function (next) {
  await Post.deleteMany({ author: this._id }); // delete all of the user's posts
  next();
});
userSchema.pre(/^find/, function (next) {
  this.where({ isDeleted: { $ne: true } }); // Exclude soft-deleted users
  next();
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, we format certain fields before saving, delete dependent data to avoid orphaned data, and exclude soft-deleted users without ever worrying about adding that condition everywhere the query is made.&lt;/p&gt;

&lt;h2&gt;
  
  
  Statics and Methods: Class-Like Behavior
&lt;/h2&gt;

&lt;p&gt;Mongoose models behave like classes, and just like in classes, you can add static methods (model-level) and instance methods (document-level):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;userSchema.statics.findByEmail = function (email) {
  return this.findOne({ email });
};
userSchema.methods.isAdult = function () {
  return this.age &amp;gt;= 18;
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So that:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const user = await User.findByEmail('xyz@yopmail.com');
console.log(user.isAdult) // true or false
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Clean, expressive, and object-oriented. &lt;/p&gt;

&lt;h2&gt;
  
  
  Bonus
&lt;/h2&gt;

&lt;p&gt;A few extra schema features that make your models better:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;match: /^[a-zA-Z0-9_-]$/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This allows you to enforce valid characters for a field using a regex.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;index: true
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Index a field to increase query performance when you query by that field. Useful when you have a field that is queried regularly. You can also use compound indexes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;unique: true
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To prevent duplicate values for a field. It also creates an index on the field.&lt;/p&gt;

&lt;p&gt;And schema-level options:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;timestamps: true
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Automatically add createdAt and updatedAt&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;expireAt: { type: Date, default: Date.now, expires: 600 }
// OR
expireAt: { type: Date, default: Date.now, expires: '10m' }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a TTL (Time-to-live) index that automatically deletes the document 10 minutes (configurable) after the expireAt field is set or after the document was created when a default value is set. Useful for one-time passwords.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;toObject: { getters: true, virtuals: true }
toJSON: { getters: true, virtuals: true }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The toJSON and toObject schema options convert the mongoose document to a JavaScript object and can also contain modifications that apply to every document in the model, with the difference being that toJSON takes effect when toJSON or JSON.stringify() is called in JavaScript code. It's important to set the getters and virtuals fields to true in both.&lt;/p&gt;

&lt;p&gt;The final model then looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const userSchema = new mongoose.schema({
  email: {
    type: String,
    set: v =&amp;gt; v.toLowerCase(),
    unique: true,
  },
  firstName: {
    type: String,
    set: v =&amp;gt; v.toLowerCase(),
    get: v =&amp;gt; toTitleCase(v), // assuming you have a toTitleCase util
  },
  lastName: {
    type: String,
    set: v =&amp;gt; v.toLowerCase(),
    get: v =&amp;gt; toTitleCase(v),
  },
  username: {
    type: String,
    match: /^[a-zA-Z0-9_-]$/,
    set: v =&amp;gt; v.toLowerCase(),
    unique: true,
  },
  age: {
    type: Number,
},
  pin: {
    type: String,
},
phoneNumber: {
    type: String,
},
},
{
  timestamps: true,
  toJSON: { getters: true, virtuals: true},
  toObject: { getters: true, virtuals: true},
});

userSchema.virtual('fullName').get(function () {
  return `${this.firstName} ${this.lastName}`;
});

userSchema.pre('save', function (next) {
  if (this.phoneNumber) {
  this.phoneNumber = formatPhoneNumber(this.phoneNumber); // format phone number to desired form.
}
  next();
});
userSchema.pre('remove', async function (next) {
  await Post.deleteMany({ author: this._id }); // delete all of the user's posts
  next();
});
userSchema.pre(/^find/, function (next) {
  this.where({ isDeleted: { $ne: true } }); // Exclude soft-deleted users
  next();
});
userSchema.statics.findByEmail = function (email) {
  return this.findOne({ email });
};
userSchema.methods.isAdult = function () {
  return this.age &amp;gt;= 18;
};

export const User = mongoose.model('User', userSchema);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Looks like the better, more functional, and more maintainable way to write production code, doesn't it?&lt;br&gt;
So, whether you prefer using it as an extra layer of validation or to enforce consistency, or to reduce boilerplate, or have simply been convinced to write cool code like this, it is very clear that Mongoose is much more powerful than most are aware of. So, don't just use it, leverage it.&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>mongodb</category>
      <category>mongoose</category>
      <category>node</category>
    </item>
  </channel>
</rss>
