API Testing in Playwright
By Syncfusion®, Inc. · Last updated
Why test APIs directly, the request fixture, sending GET, POST, PUT, PATCH and DELETE requests with JSON, form data, query params and headers, checking status, headers and JSON with toBeOK and toMatchObject, API authentication with tokens and cookies, a full create-read-update-delete flow, error cases, separate request contexts, and mixing API and UI steps in one test.
API Testing
- Behind most web pages is an API: a set of URLs that send and receive data, usually as JSON.
- Playwright can call that API directly, with no browser page at all. These tests are very fast.
- In this lesson, you will test the Practice Shop's API, and use it to set up data for UI tests.
Simple lesson: an API test sends a request and checks the answer. No page, no clicks.
A Real-World Example
- The shop's API has a product endpoint:
GET /api/products/2. - This test asks for product 2 and checks the answer. It needs no page, so it finishes in milliseconds.
- The test uses the
requestfixture instead ofpage. No browser page opens.
What You Will Learn
- Why API tests are useful, and how they fit with UI tests.
- The
requestfixture and the five common methods. - Checking status codes, headers and JSON.
- Sending JSON, form data, query params and headers.
- Logging in to an API with a token or a cookie.
- A full create, read, update, delete flow, with error cases.
- Using the API to prepare data for UI tests.
Why Test the API
Faster and More Focused
- A UI test loads pages, waits for elements and clicks. An API test sends one request.
- API tests are often 10 to 100 times faster. You can run many more of them.
- They check the rules of the app directly: prices, validation, permissions.
- UI tests are still needed. They check what the user actually sees and does.
The Shop's API
| Method | URL | What it does |
|---|---|---|
GET | /api/products | List products. ?category=Fitness filters them |
GET | /api/products/:id | One product |
POST | /api/login | Log in. Returns a token and sets a cookie |
GET | /api/me | The logged-in user |
GET | /api/orders | Your orders |
POST | /api/orders | Place an order |
GET, PATCH, PUT, DELETE | /api/orders/:id | Read, change, replace or delete one order |
Simple lesson: test the rules through the API. Test what users see through the UI.
The request Fixture
Sending Requests
request.get(url),request.post(url),request.put(url),request.patch(url)andrequest.delete(url)send the five common methods.- Relative URLs use
baseURLfrom the config, just likepage.goto(). - Each call returns an API response.
Animation: the test sends POST /api/orders with a JSON body straight to the server. The server answers 201 Created with the new order as JSON. No browser page takes part
Your test request.post()
Server API /api/orders
POST productId: 1 201 id: 1004, …
An API test talks to the server directly. There is no page in between.
Checking the Response
await expect(response).toBeOK()passes for any status from 200 to 299.response.status()gives the exact code.response.json()reads the body.response.headers()gives the headers, with lower-case names.toMatchObject()checks some fields and ignores the rest.expect.any(Number)accepts any number, for IDs you cannot know in advance.
paramsadds the query string for you:?category=Fitness.
toBeOK() is a web-first style matcher, so write await before it. On failure it prints the status and the start of the body, which often explains the problem.
Simple lesson: toBeOK() for "it worked", status() for the exact code, json() for the data.
Sending Data
JSON, Forms and Headers
data: { ... }sends a JSON body and setsContent-Type: application/json.form: { ... }sends a classic HTML form body.headers: { ... }adds headers to one request.- The shop's login accepts both JSON and forms.
Authenticated APIs
Tokens in Headers
- Many APIs want a token in the
Authorizationheader:Bearer <token>. - Set it once for a whole file with
test.use({ extraHTTPHeaders: { ... } }), or for the whole project in the config'suse.
- In a real project, the token comes from the login call or an environment variable, never written in the test.
Cookies Carry Over
- The
requestfixture keeps cookies between calls in one test, like a browser. - Log in once with
POST /api/login, and later calls are logged in through the cookie.
- Since Playwright 1.64, a request context can also manage cookies directly:
request.addCookies([...]),request.cookies()andrequest.clearCookies(), like a browser context.
Simple lesson: send a token in a header, or log in once and let the cookie do the rest.
A Full CRUD Flow
Create, Read, Update, Delete
- CRUD means create, read, update and delete. Most APIs follow this pattern.
POSTcreates and usually answers 201 Created.GETreads.PATCHchanges some fields.PUTreplaces the whole thing.DELETEremoves it, often with 204 No Content.- This test follows one order through its whole life.
Testing the Error Cases
- Good API tests check the unhappy paths too. They are where bugs hide.
- 400 or 422: bad input. 401: not logged in. 403: logged in, but not allowed. 404: not found. 409: a conflict, like changing a delivered order.
Tests that create data should clean it up, or use data that cannot clash. The practice API resets for every test. A real test server usually does not, so delete what you create, often in afterEach.
Simple lesson: test each status code your API promises, not only 200.
Request Contexts and Options
Your Own Request Context
- The
requestfixture is made fresh for each test. - For setup work outside a test, like
beforeAll, make your own withrequest.newContext()from@playwright/test. - Give it a
baseURLand headers. Calldispose()when done.
failOnStatusCode
- By default, a 404 or 500 does not throw. You check the status yourself.
failOnStatusCode: truemakes any non-2xx status throw at once, with the status in the error.- Use it for setup calls that must work. Leave it off when you are testing error codes.
A setup call that fails quietly makes the real check fail later, with a confusing message. failOnStatusCode: true stops the test at the call that broke.
Mixing API and UI
API for Setup, UI for the Check
- Creating test data through the UI is slow. Creating it through the API is fast.
page.requestis a request context that shares cookies with the page. Log in or create data with it, then open the page.
- The reverse also works: act in the UI, then check the result through the API.
Simple lesson: set up with the API, check with the UI. Each part does what it is best at.
Try It in Your Project
- These steps take the lesson from the lab to your own app.
- Write API tests for your three most important endpoints, including one error status each.
- Replace one slow UI data setup with an API call through
page.request. - Clean up the data your tests create, or use unique names per run.
Simple lesson: use one idea from this lesson on your own app this week.
TL;DR
- The
requestfixture sendsget,post,put,patchanddeletewith no page. await expect(response).toBeOK(),status(),headers()andjson()check the answer.datasends JSON,formsends forms,paramsbuilds the query string.- Tokens go in
extraHTTPHeaders. Login cookies carry over between calls. - Test every status code: 201, 204, 401, 403, 404, 409, 422.
page.requestshares cookies with the page. Use it to set up data for UI tests.
Common mistakes
| Mistake | What happens | Fix |
|---|---|---|
expect(response).toBeOK() without await | The check may not run before the test ends | Write await expect(response).toBeOK() |
| Only testing 200 answers | Validation and permission bugs go unseen | Add tests for 4xx codes |
| Expecting a 404 to throw | It does not. The test carries on with bad data | Check status(), or use failOnStatusCode: true |
Checking the whole JSON with toEqual when IDs change | The test breaks on every run | toMatchObject with expect.any(Number) |
| Tokens written in the test file | Secrets leak into git | Read them from environment variables |
Check Yourself
Exercises
What to Remember
requestsends API calls with no page. They are fast and focused.await expect(response).toBeOK(), thenstatus()andjson().data,form,paramsandheadersshape the request.- Tokens in
extraHTTPHeaders. Cookies carry over after a login. - Test the error codes, and clean up data you create.
- API for setup, UI for what users see.
Next: Page Object Model