In this article, I’ll walk through how I built a Playwright-based API automation framework using JavaScript, using the Swagger Petstore API as an example.
🚀 Why Playwright for API Testing?
Playwright is widely known for UI testing, but it also provides powerful API testing capabilities:
- Built-in API request support
- No need for external libraries like Axios
- Integrated with test runner and reporting
- Supports fixtures and parallel execution
📁 Project Structure
Here’s the folder structure used in this framework:
playwright/
├── api/ # API layer (service classes)
├── tests/ # Test specs
├── fixtures/ # Custom Playwright fixtures
├── utils/ # Helpers (logger, data loader)
├── config/ # Environment configs
├── constants/ # Endpoints and constants
├── test-data/ # JSON payloads
├── playwright.config.js🔧 API Layer Design
Instead of writing HTTP calls directly in tests, we use a service layer.
Example: PetAPI
export class PetAPI {
constructor(request, baseURL) {
this.request = request;
this.baseURL = baseURL;
}
async createPet(payload) {
return await this.request.post('/pet', {
data: payload
});
}
}🧪 Writing Clean Test Cases
test('Create a pet', async ({ apiContext }) => {
const response = await apiContext.post('/pet', {
data: {
id: Date.now(),
name: 'Doggie',
status: 'available'
}
});
expect(response.status()).toBe(200);
});With fixtures, we inject a reusable API context into tests.
🔌 Custom API Fixture
Playwright fixtures allow us to centralize configuration:
const test = base.extend({
apiContext: async ({ request }, use) => {
const apiContext = await request.newContext({
baseURL: 'https://petstore.swagger.io/v2'
});
await use(apiContext);
await apiContext.dispose();
},
});🌍 Environment Management
We support multiple environments (dev, stage, UAT) using an environment manager:
const ENV = process.env.ENV || 'dev';Each environment has its own config file:
export default {
name: 'dev',
baseURL: 'https://petstore.swagger.io/v2'
};📦 Test Data Management
Instead of hardcoding payloads, we store them in JSON files:
{
"id": "{{id}}",
"name": "Doggie",
"status": "available"
}Then dynamically replace values:
const payload = replacePlaceholders(data, {
id: Date.now()
});📊 Central Logging
A reusable logger helps debug tests easily:
logRequest('POST', url, payload);
logResponse(response);Sample Output:
--- API REQUEST ---
POST https://petstore.swagger.io/v2/pet🧠 Key Design Principles
- Separation of concerns (tests vs API vs data)
- Reusability and modular design
- Environment-driven configuration
- Clean and readable test cases
🎯 Conclusion
Building a scalable API test framework is not just about writing tests—it’s about designing a system that is:
- Maintainable
- Extendable
- Easy to debug
Using Playwright, we can achieve all of this with minimal dependencies and a clean architecture.
🔗 Resources
- GitHub Project: https://github.com/BathiyaL/tec-nimbus/tree/main/api-test-tools/playwright
- Swagger API: https://petstore.swagger.io/

