Most API clients trap your test logic inside request files. Bruno takes a modern and dev-first...
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!
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.
- Navigate to your Bruno collection folder:
cd path/to/your/bruno-collection
- Initialize a
package.json
file:npm init -y
- 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!
✅ 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! 🐶