Skip to content

External Libraries in Bruno: Exploring Zod Schema Validation

API Clients are usually powerful… until you try to use the tools you already know—like external libraries (or...cough... Git).

Other platforms require workarounds, sandboxes, and script injections to get basic utilities working. But Bruno takes a different approach.

Bruno runs your test scripts in a real Node.js environment.
That means you can npm install your favorite packages and require() them like you would in any local project.

In this post, we’ll walk through:

  • 💡 Why using external libraries in your test scripts matters
  • 🔍 What Zod is and why it’s useful
  • 🧪 Real-world examples using a pet-themed API and zod
  • 🛠 How to install libraries into your Bruno collection safely

Let’s dive in!

Leveraging external libraries requires using Developer Mode. It's important to be cautious when using this mode as it allows script execution on your machine. Only use Developer Mode when you trust the author of the collection (or if you are the author, of course).

🤔 Why Use External Libraries in API Tests?

Real-world APIs can get complex: nested responses, dynamic data, or custom validations. Instead of writing that logic from scratch, external libraries give you reliable, well-tested tools to:

  • Format or manipulate data (dayjs)
  • Validate conditions (joi, zod)
  • Generate dynamic content (uuid, faker)
  • Do complex math or logic (mathjs)

In Bruno, you don’t need to hack these in. You just install them.

Bonus: Bruno also includes built-in libraries like chai, axios, ajv, and lodash — no install needed. Learn more in our inbuilt libraries docs.


📚 What Is Zod?

zod is a TypeScript-first schema validation library for JavaScript. It’s lightweight, powerful, and works great for validating API responses in tests.

Here’s what Zod is great at:

  • Defining strict schemas for expected objects
  • Parsing and validating incoming data
  • Throwing clear, readable errors when validation fails

📦 Installing External Libraries in Bruno

To use an external package like zod in your test scripts, you'll first need to initialize a package.json file in your Bruno collection directory and install the package.

  1. Navigate to your Bruno collection folder:
    cd path/to/your/bruno-collection
  2. Initialize a package.json file:
    npm init -y
  3. Install the external package:
    npm install zod

Bruno will now be able to use the installed package inside your test scripts just like any regular Node.js project.


🐾 Real-World Examples

Let’s walk through two playful but practical examples using Bruno, Zod, and our echo server. Each request sends a pet-related payload, and we validate the responses with Zod-powered tests.

You can follow along and create your own or grab the collection directly from GitHub by clicking the button below: 

 

📄 Request 1: Validating Response for Name and Type

This one’s simple: we’re validating that each animal in the response has a name and a recognized type (dog, cat, or bird).

{
  "users": [
    { "name": "Bruno", "type": "dog" },
    { "name": "Luna", "type": "cat" },
    { "name": "Ziggy", "type": "bird" }
  ]
}

Test Script:

const { z } = require("zod");
const response = res.getBody();

const UserSchema = z.object({
  name: z.string(),
  type: z.enum(["dog", "cat", "bird"])
});

const Schema = z.object({
  users: z.array(UserSchema)
});

test("Validate response schema structure", () => {
  Schema.parse(response);
});

 

Perfect! Looks like we're returning the proper structure in our response.

 

📄 Request 2: Detailed Pet Validation 

Let’s add some extra validation here. We’re not only validating the response structure, but also asserting that all pets are vaccinated. Bruno doesn't want to get kennel cough! 🐶

{
  "pets": [
    {
      "id": "pet_001",
      "name": "Bruno",
      "age": 4,
      "vaccinated": true,
      "tags": ["friendly", "energetic"]
    },
    {
      "id": "pet_002",
      "name": "Luna",
      "age": 2,
      "vaccinated": false,
      "tags": ["quiet"]
    }
  ]
}

Test Scripts:

const { z } = require("zod");
const response = res.getBody();

const PetSchema = z.object({
  id: z.string().startsWith("pet_"),
  name: z.string(),
  age: z.number().int().nonnegative(),
  vaccinated: z.boolean(),
  tags: z.array(z.string())
});

const PetsResponseSchema = z.object({
  pets: z.array(PetSchema)
});

test("Valid response structure for pets", () => {
  PetsResponseSchema.parse(response);
});

test("All pets must be vaccinated", () => {
  const unvaccinated = response.pets.filter(pet => !pet.vaccinated);
  if (unvaccinated.length > 0) {
    throw new Error(`Found unvaccinated pets: ${unvaccinated.map(p => p.name).join(", ")}`);
  }
});

 

Looks like Luna may not be the best play date for Bruno 😔 , but we are getting a valid response structure for the pet details!

 

Test Results


✅ Why Bruno’s Approach Wins

Here’s what makes this experience better than other tools:

Feature Bruno Others
Use require() like normal Node.js
Install npm packages
Supports any external module (even private) Limited
Local-first (no cloud storage of secrets/code)

No sandboxes. No evals. No headaches. 🎉


🔗 Try the Demo Yourself

We’ve published the full working collection on GitHub. You can either clone this, or simply click the Fetch in Bruno button below!

 

You’ll see the request go out to the echo server, the response come back, and your Zod-powered tests validate the response schema.


💬 Final Thoughts

Using external libraries in test scripts shouldn’t be a hassle. Bruno treats your code like actual code—because it is.

We’d love to see what libraries you end up using in Bruno collections. Got something cool to share? Tag us or contribute to docs.usebruno.com.

Happy testing! 🐶