REST and GraphQL API testing with Playwright. Use when testing APIs, mocking endpoints, validating responses, or integrating API tests with E2E flows.
Comprehensive API testing for REST and GraphQL endpoints using Playwright's built-in API testing capabilities.
import { test, expect } from '@playwright/test';
test('GET /api/users returns users', async ({ request }) => {
const response = await request.get('/api/users');
expect(response.ok()).toBeTruthy();
expect(response.status()).toBe(200);
const users = await response.json();
expect(users).toHaveLength(10);
expect(users[0]).toHaveProperty('email');
});
# Playwright includes API testing - no extra packages needed
npm install -D @playwright/test
playwright.config.ts:
import { defineConfig } from '@playwright/test';
export default defineConfig({
testDir: './tests',
use: {
baseURL: 'http://localhost:3000',
extraHTTPHeaders: {
'Accept': 'application/json',
},
},
projects: [
{
name: 'api',
testMatch: /.*\.api\.spec\.ts/,
},
{
name: 'e2e',
testMatch: /.*\.e2e\.spec\.ts/,
use: { browserName: 'chromium' },
},
],
});
test('fetch user by ID', async ({ request }) => {
const response = await request.get('/api/users/123');
expect(response.ok()).toBeTruthy();
const user = await response.json();
expect(user.id).toBe(123);
expect(user.email).toMatch(/@/);
});
test('create new user', async ({ request }) => {
const response = await request.post('/api/users', {
data: {
name: 'John Doe',
email: 'john@example.com',
},
});
expect(response.status()).toBe(201);
const user = await response.json();
expect(user.id).toBeDefined();
expect(user.name).toBe('John Doe');
});
test('update user', async ({ request }) => {
const response = await request.put('/api/users/123', {
data: {
name: 'Jane Doe',
},
});
expect(response.ok()).toBeTruthy();
const user = await response.json();
expect(user.name).toBe('Jane Doe');
});
test('delete user', async ({ request }) => {
const response = await request.delete('/api/users/123');
expect(response.status()).toBe(204);
// Verify deletion
const getResponse = await request.get('/api/users/123');
expect(getResponse.status()).toBe(404);
});
test.describe('authenticated requests', () => {
let token: string;
test.beforeAll(async ({ request }) => {
const response = await request.post('/api/auth/login', {
data: {
email: 'test@example.com',
password: 'password123',
},
});
const data = await response.json();
token = data.token;
});
test('access protected endpoint', async ({ request }) => {
const response = await request.get('/api/protected', {
headers: {
'Authorization': `Bearer ${token}`,
},
});
expect(response.ok()).toBeTruthy();
});
});
test('login and access dashboard', async ({ request, context }) => {
// Login (sets cookie automatically)
await request.post('/api/auth/login', {
data: { email: 'test@example.com', password: 'pass' },
});
// Cookie is automatically included in subsequent requests
const response = await request.get('/api/dashboard');
expect(response.ok()).toBeTruthy();
});
test('GraphQL query users', async ({ request }) => {
const response = await request.post('/graphql', {
data: {
query: `
query GetUsers {
users {
id
name
email
}
}
`,
},
});
const { data, errors } = await response.json();
expect(errors).toBeUndefined();
expect(data.users).toHaveLength(10);
});
test('GraphQL create user', async ({ request }) => {
const response = await request.post('/graphql', {
data: {
query: `
mutation CreateUser($input: CreateUserInput!) {
createUser(input: $input) {
id
name
email
}
}
`,
variables: {
input: {
name: 'John Doe',
email: 'john@example.com',
},
},
},
});
const { data, errors } = await response.json();
expect(errors).toBeUndefined();
expect(data.createUser.id).toBeDefined();
});
test('GraphQL with auth', async ({ request }) => {
const response = await request.post('/graphql', {
headers: {
'Authorization': `Bearer ${token}`,
},
data: {
query: `
query Me {
me {
id
email
role
}
}
`,
},
});
const { data } = await response.json();
expect(data.me.role).toBe('admin');
});
test('display mocked products', async ({ page }) => {
// Mock the API
await page.route('**/api/products', route => {
route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify([
{ id: 1, name: 'Mock Product', price: 99.99 },
]),
});
});
await page.goto('/products');
await expect(page.locator('.product')).toHaveCount(1);
await expect(page.locator('.product-name')).toHaveText('Mock Product');
});
test('handle API error gracefully', async ({ page }) => {
await page.route('**/api/products', route => {
route.fulfill({
status: 500,
body: JSON.stringify({ error: 'Server error' }),
});
});
await page.goto('/products');
await expect(page.locator('.error-message')).toBeVisible();
});
test('show loading state', async ({ page }) => {
await page.route('**/api/products', async route => {
await new Promise(r => setTimeout(r, 2000));
route.fulfill({
status: 200,
body: JSON.stringify([]),
});
});
await page.goto('/products');
await expect(page.locator('.loading-spinner')).toBeVisible();
});
import Ajv from 'ajv';
const ajv = new Ajv();
const userSchema = {
type: 'object',
properties: {
id: { type: 'number' },
name: { type: 'string' },
email: { type: 'string', format: 'email' },
},
required: ['id', 'name', 'email'],
};
test('validate response schema', async ({ request }) => {
const response = await request.get('/api/users/1');
const user = await response.json();
const validate = ajv.compile(userSchema);
expect(validate(user)).toBeTruthy();
});
test('check response headers', async ({ request }) => {
const response = await request.get('/api/users');
expect(response.headers()['content-type']).toContain('application/json');
expect(response.headers()['x-rate-limit-remaining']).toBeDefined();
});
import path from 'path';
test('upload file', async ({ request }) => {
const response = await request.post('/api/upload', {
multipart: {
file: {
name: 'test.pdf',
mimeType: 'application/pdf',
buffer: Buffer.from('PDF content'),
},
description: 'Test document',
},
});
expect(response.ok()).toBeTruthy();
const result = await response.json();
expect(result.filename).toBe('test.pdf');
});
afterEach or afterAll hooksreferences/graphql-testing.md - Advanced GraphQL patternsreferences/schema-validation.md - JSON Schema with Ajv