Many end-to-end scenarios involve both UI and API interactions. A service layer of helpers can centralise API calls, test data creation and environment operations so your Playwright tests stay clean and focused.
Creating Service Helpers for APIs
Service helpers are modules that wrap backend endpoints in descriptive functions. Tests can then call these helpers to set up state or verify behaviour without duplicating HTTP logic.
// services/orders-service.ts
import { APIRequestContext, expect } from '@playwright/test';
export class OrdersService {
constructor(private readonly api: APIRequestContext) {}
async createOrder(payload: any) {
const response = await this.api.post('/orders', { data: payload });
expect(response.ok()).toBeTruthy();
return response.json();
}
async getOrder(id: string) {
const response = await this.api.get(`/orders/${id}`);
expect(response.ok()).toBeTruthy();
return response.json();
}
}
// orders-flow.spec.ts
import { test, request, expect } from '@playwright/test';
import { OrdersService } from './services/orders-service';
test('shows newly created order in UI', async ({ page }) => {
const apiContext = await request.newContext({ baseURL: 'https://api.demo.myshop.com' });
const orders = new OrdersService(apiContext);
const order = await orders.createOrder({ total: 123.45, currency: 'USD' });
await page.goto('https://demo.myshop.com/orders');
await expect(page.getByText(order.id)).toBeVisible();
});
This pattern allows tests to set up complex scenarios quickly via APIs and then verify key outcomes through the UI.
Common Mistakes
Mistake 1 β Duplicating HTTP calls directly in many tests
This is hard to maintain.
β Wrong: Copy-pasting raw apiContext.post calls across files.
β Correct: Centralise calls in service helpers that can evolve with the API.
Mistake 2 β Mixing service logic into page objects
This blurs layers.
β Wrong: Having page objects create or modify backend state directly.
β Correct: Keep a clear separation: page objects for UI, services for APIs.