From REST APIs to API + UI Automation
When most testers hear Playwright, the first thing that comes to mind is browser automation.But Playwright is not limited to browser automation. It also provides an API testing capability that allows you to send HTTP requests directly to your application’s backend without opening a browser. And this becomes particularly interesting when you are already using Playwright with Java for UI automation. Instead of maintaining one framework for UI testing and another framework for API testing, you can use Playwright to handle both.
With Playwright’s APIRequestContext, you can:
- Send REST API requests
- Validate HTTP status codes
- Validate response headers and bodies
- Send request payloads
- Handle authentication
- Create test data through APIs
- Prepare backend state before UI tests
- Validate backend state after UI actions
- Combine API and UI automation in the same test
This makes Playwright a powerful option for building full-stack automated tests.
In this article, we will explore API Testing with Playwright using Java with practical examples.
What Is API Testing with Playwright?
Playwright provides the APIRequestContext API for sending HTTP requests directly to an application.
You can create an isolated API request context using:
APIRequestContext request = playwright.request().newContext();
From there, you can perform operations such as:
GETPOSTPUTPATCHDELETE
Playwright’s Java API provides corresponding methods such as get(), post(), put(), patch(), and delete().
The important point is that you don’t need a browser to perform these API calls.
For example:
APIResponse response = request.get("/users");
The response can then be inspected and validated.
Setting Up Playwright API Testing with Java
Let’s assume we are using:
- Java
- Maven
- Playwright
- JUnit 5
Add Playwright to your Maven project.
<dependency> <groupId>com.microsoft.playwright</groupId> <artifactId>playwright</artifactId> <version>YOUR_PLAYWRIGHT_VERSION</version></dependency>
You can use the current Playwright Java version appropriate for your project rather than hardcoding an outdated version into your framework.
Playwright’s official Java API supports creating an APIRequestContext through Playwright.request().newContext().
Creating an APIRequestContext
A basic API test setup looks like this:
import com.microsoft.playwright.APIRequest;import com.microsoft.playwright.APIRequestContext;import com.microsoft.playwright.APIResponse;import com.microsoft.playwright.Playwright;public class ApiTest { private Playwright playwright; private APIRequestContext request; public void setUp() { playwright = Playwright.create(); request = playwright.request().newContext( new APIRequest.NewContextOptions() .setBaseURL("https://api.example.com") ); } public void tearDown() { request.dispose(); playwright.close(); }}
Using setBaseURL() is useful because you don’t need to repeatedly specify the complete URL.
Instead of:
request.get("https://api.example.com/users");
you can write:
request.get("/users");
Playwright resolves the relative URL against the configured base URL.
GET Request with Playwright
Let’s start with a simple GET request.
@Testvoid getUsers() { APIResponse response = request.get("/users"); System.out.println(response.status()); System.out.println(response.text());}
The APIResponse object provides access to information such as:
- Status code
- Response body
- Headers
- Status text
For example:
System.out.println(response.status());System.out.println(response.statusText());System.out.println(response.text());
Playwright’s APIResponse represents responses returned from APIRequestContext methods such as get() and post().
Validating the HTTP Status Code
Sending an API request is only half the job.
The real value of API testing comes from validating the response.
Using JUnit 5:
import static org.junit.jupiter.api.Assertions.assertEquals;@Testvoid shouldReturnUsers() { APIResponse response = request.get("/users"); assertEquals(200, response.status());}
This verifies that the API returned HTTP 200 OK.
You can also validate other expected responses:
assertEquals(201, response.status());
for resource creation,
or:
assertEquals(401, response.status());
for an authentication failure scenario.
Validating the Response Body
The response body can be retrieved using:
String responseBody = response.text();System.out.println(responseBody);
Suppose the API returns:
{ "id": 101, "name": "John", "email": "john@example.com"}
You could perform a basic validation:
assertTrue(response.text().contains("John"));
However, string-based validation can become fragile.
For real-world automation frameworks, it is better to parse JSON and validate specific fields.
For example, using Jackson:
ObjectMapper objectMapper = new ObjectMapper();JsonNode jsonResponse = objectMapper.readTree(response.text());assertEquals("John", jsonResponse.get("name").asText());assertEquals("john@example.com", jsonResponse.get("email").asText());
This approach allows you to validate individual JSON properties instead of searching the entire response as a string.
Validating Response Headers
API testing isn’t only about the response body.
Headers are equally important.
For example:
Map<String, String> headers = response.headers();System.out.println(headers);
You can validate a particular header:
assertEquals( "application/json", response.headers().get("content-type"));
Depending on the API and framework requirements, you might also validate:
Content-TypeCache-ControlLocation- Security headers
- Correlation IDs
- Custom application headers
Playwright’s APIResponse.headers() provides the response headers as a map.
POST Request with Playwright
Now let’s create a resource using a POST request.
Suppose our API expects:
{ "name": "John Doe", "email": "john@example.com"}
We can create the request body using a Java Map.
Map<String, Object> requestBody = new HashMap<>();requestBody.put("name", "John Doe");requestBody.put("email", "john@example.com");
Then send the request:
APIResponse response = request.post( "/users", RequestOptions.create() .setData(requestBody));
Playwright’s Java API supports passing JSON-compatible data through RequestOptions.setData().
Now validate the response:
assertEquals(201, response.status());
And inspect the returned user:
System.out.println(response.text());
Using POJOs for API Request Bodies
For a larger automation framework, creating request bodies with Map<String, Object> everywhere can become difficult to maintain.
A better approach is to create Java POJOs.
For example:
public class User { private String name; private String email; public User(String name, String email) { this.name = name; this.email = email; } public String getName() { return name; } public String getEmail() { return email; }}
Then serialize the object using Jackson:
ObjectMapper objectMapper = new ObjectMapper();User user = new User( "John Doe", "john@example.com");String requestBody = objectMapper.writeValueAsString(user);
Then send it:
APIResponse response = request.post( "/users", RequestOptions.create() .setHeader("Content-Type", "application/json") .setData(requestBody));
This approach scales much better when request models become complex.
API Authentication with Playwright
Authentication is one of the most important parts of API testing.
Imagine an API requiring:
Authorization: Bearer <token>
We can configure the authorization header when creating the API request context.
Map<String, String> headers = new HashMap<>();headers.put( "Authorization", "Bearer " + token);headers.put( "Content-Type", "application/json");
Then:
request = playwright.request().newContext( new APIRequest.NewContextOptions() .setBaseURL("https://api.example.com") .setExtraHTTPHeaders(headers));
Now every request made using this context can use the configured headers.
Playwright’s official API testing documentation demonstrates configuring authorization headers on an APIRequestContext.
Testing Authentication APIs
You can also test the authentication endpoint itself.
For example:
Map<String, Object> loginRequest = new HashMap<>();loginRequest.put("username", "testuser");loginRequest.put("password", "password123");
Send the request:
APIResponse response = request.post( "/login", RequestOptions.create() .setData(loginRequest));
Validate:
assertEquals(200, response.status());
Then extract the token:
JsonNode jsonResponse = objectMapper.readTree(response.text());String token = jsonResponse.get("token").asText();
That token can then be used for subsequent API calls.
This creates a simple API authentication flow:
Login API ↓Extract Token ↓Create Authenticated API Context ↓Call Protected API ↓Validate Response
Negative API Testing
A good API test suite should not only test successful scenarios.
Negative scenarios are equally important.
For example:
Invalid authentication
assertEquals(401, response.status());
Invalid request data
assertEquals(400, response.status());
Resource not found
assertEquals(404, response.status());
Forbidden operation
assertEquals(403, response.status());
You can also validate the error response:
JsonNode error = objectMapper.readTree(response.text());assertEquals( "Invalid credentials", error.get("message").asText());
This makes the API test suite much more meaningful than simply checking whether the endpoint returns 200.
API Testing with Query Parameters
Playwright also supports query parameters.
For example:
GET /users?page=2
Using Java:
APIResponse response = request.get( "/users", RequestOptions.create() .setQueryParam("page", "2"));
You can then validate:
assertEquals(200, response.status());
Playwright’s Java RequestOptions supports configuring query parameters for API requests.
API + UI Test Chaining
This is where Playwright becomes particularly interesting.
Imagine your UI test requires a user to exist before the test starts.
Traditionally, you might:
- Open the application
- Navigate to registration
- Fill in the form
- Submit the form
- Wait for the user to be created
- Continue with the actual test
That’s a lot of UI interaction just to create test data.
With Playwright API testing, we can create the user directly through the backend.
API ↓Create User ↓Browser ↓Login ↓Perform UI Test
For example:
APIResponse response = request.post( "/users", RequestOptions.create() .setData(userData));assertEquals(201, response.status());
Then launch the browser:
Browser browser = playwright.chromium().launch();BrowserContext context = browser.newContext();Page page = context.newPage();
Now navigate to the application:
page.navigate("https://example.com");
The API has prepared the required state before the UI test begins.
Playwright’s official documentation specifically describes using API requests to prepare server-side state before visiting a web application and to validate server-side post-conditions after browser actions.
UI Test Followed by API Validation
The reverse is also extremely useful.
Suppose the UI performs an action:
User clicks "Create Order"
Instead of validating everything through the UI, we can verify the backend state using an API.
UI Action ↓Create Order ↓API Request ↓GET /orders/{id} ↓Validate Order
This gives us a much stronger end-to-end test.
For example:
page.getByRole( AriaRole.BUTTON, new Page.GetByRoleOptions().setName("Create Order")).click();
Then:
APIResponse response = request.get("/orders/" + orderId);assertEquals(200, response.status());
Now we are validating both sides of the application:
Frontend behavior + backend state.
Sharing Authentication Between API and UI
One of the more powerful Playwright capabilities is that API and browser contexts can share authentication state.
Playwright documents that storage state can be obtained from an authenticated APIRequestContext and then used when creating a BrowserContext.
For example:
APIRequestContext requestContext = playwright.request().newContext();requestContext.get("/login");String state = requestContext.storageState();
That state can then be used when creating a browser context:
BrowserContext context = browser.newContext( new Browser.NewContextOptions() .setStorageState(state) );
Conceptually:
API Login ↓Authenticated State ↓Browser Context ↓Already Authenticated UI
This can eliminate repetitive UI login steps and make test execution significantly faster.
APIRequestContext vs BrowserContext.request()
There are two useful approaches in Playwright.
Standalone API context
APIRequestContext request = playwright.request().newContext();
This gives you an isolated API request context.
Browser context request
You can also access the request context associated with a browser context:
APIRequestContext request = context.request();
A request context obtained from a BrowserContext shares the browser context’s cookie jar.
This is useful when API and UI interactions need to operate with the same session.
Playwright documents both approaches and notes that Page.request() is a shortcut for the request context associated with the page’s browser context.
A Practical API Test Class
Let’s combine some of these concepts into a simple JUnit 5 example.
import com.fasterxml.jackson.databind.JsonNode;import com.fasterxml.jackson.databind.ObjectMapper;import com.microsoft.playwright.*;import org.junit.jupiter.api.*;import java.util.HashMap;import java.util.Map;import static org.junit.jupiter.api.Assertions.*;@TestInstance(TestInstance.Lifecycle.PER_CLASS)public class UserApiTest { private Playwright playwright; private APIRequestContext request; private ObjectMapper objectMapper; @BeforeAll void setup() { playwright = Playwright.create(); objectMapper = new ObjectMapper(); request = playwright.request().newContext( new APIRequest.NewContextOptions() .setBaseURL("https://api.example.com") .setExtraHTTPHeaders( Map.of( "Content-Type", "application/json" ) ) ); } @Test void shouldCreateUser() throws Exception { Map<String, Object> user = new HashMap<>(); user.put("name", "John Doe"); user.put("email", "john@example.com"); APIResponse response = request.post( "/users", RequestOptions.create() .setData(user) ); assertEquals(201, response.status()); JsonNode jsonResponse = objectMapper.readTree(response.text()); assertEquals( "John Doe", jsonResponse.get("name").asText() ); } @AfterAll void tearDown() { if (request != null) { request.dispose(); } if (playwright != null) { playwright.close(); } }}
This structure gives us a clean starting point for building a reusable API automation framework.
Recommended Project Structure
For a real-world Playwright + Java automation framework, I would avoid putting all API logic directly inside test classes.
A cleaner structure could be:
src └── test └── java ├── config │ └── ConfigManager.java │ ├── api │ ├── ApiClient.java │ ├── UserApi.java │ └── AuthApi.java │ ├── models │ ├── User.java │ └── LoginRequest.java │ ├── pages │ ├── LoginPage.java │ └── DashboardPage.java │ └── tests ├── UserApiTest.java ├── LoginTest.java └── UserJourneyTest.java
The idea is to separate:
API Layer ↓Models ↓UI Page Objects ↓Tests
This makes the framework easier to maintain as the test suite grows.
Why Use Playwright for API Testing?
There are already excellent API testing tools available.
So why would a team consider using Playwright?
The biggest advantage is unification.
If your team is already using Playwright for browser automation, you can use the same ecosystem for API testing.
Instead of:
Selenium → UIREST Assured → API
you could have:
Playwright → UI + API
That can simplify:
- Framework maintenance
- Authentication handling
- Test data creation
- API + UI workflows
- CI/CD execution
- Test project structure
- Developer onboarding
However, that doesn’t mean Playwright automatically replaces every API-specific framework.
If your organization has a mature REST Assured framework with extensive custom utilities, reporting, schema validation, contract testing, and integrations, migrating everything just because Playwright supports API testing may not be worthwhile.
The decision should be based on the requirements of your automation architecture.
Playwright API Testing vs REST Assured
For Java automation engineers, one obvious comparison is Playwright API Testing vs REST Assured.
| Feature | Playwright | REST Assured |
|---|---|---|
| REST API testing | Yes | Yes |
| GET/POST/PUT/DELETE | Yes | Yes |
| Request headers | Yes | Yes |
| Authentication | Yes | Yes |
| JSON validation | Yes | Yes |
| API + UI testing | Excellent | Requires separate UI tool |
| Browser automation | Excellent | No |
| Java support | Yes | Yes |
| API-focused ecosystem | Growing | Very mature |
| Full-stack Playwright workflow | Excellent | Requires additional framework |
The biggest differentiator is not simply whether both can send a GET request.
They can.
The more interesting question is:
Do you want your API and UI automation to live in the same testing ecosystem?
If the answer is yes, Playwright becomes a compelling option.
Best Practices for API Testing with Playwright
When building a Playwright API automation framework, keep the following practices in mind.
1. Don’t hardcode URLs
Use:
.setBaseURL(baseUrl)
and environment-specific configuration.
For example:
DEVQASTAGINGPROD
should not require modifying test code.
2. Don’t hardcode credentials
Avoid:
String token = "my-secret-token";
Use environment variables or a secure secrets-management mechanism.
For example:
String token = System.getenv("API_TOKEN");
3. Create reusable API clients
Instead of:
request.post(...)
inside every test, create classes such as:
UserApiOrderApiAuthApiProductApi
For example:
public class UserApi { private final APIRequestContext request; public UserApi(APIRequestContext request) { this.request = request; } public APIResponse createUser( Map<String, Object> user) { return request.post( "/users", RequestOptions.create() .setData(user) ); }}
Now the test becomes much cleaner:
APIResponse response = userApi.createUser(user);
4. Validate more than status codes
Don’t stop at:
assertEquals(200, response.status());
Also validate:
- Response body
- JSON fields
- Headers
- Business rules
- Error messages
- Data relationships
A 200 OK response does not automatically mean the API behaved correctly.
5. Use API calls for test-data setup
If creating test data through the UI takes 30 seconds, but the same operation takes 200 milliseconds through an API, don’t make every test pay the UI cost.
Use the API to prepare the state.
Then use the UI to test the actual user journey.
Conclusion
API testing doesn’t have to live in a completely separate automation world.
With Playwright + Java, API and UI automation can work together in the same framework.
Use APIs to create data.
Use APIs to authenticate.
Use the UI to validate user journeys.
Use APIs again to verify backend state.
That combination can produce faster, cleaner, and more reliable end-to-end tests.
And that is where API Testing with Playwright becomes genuinely interesting.
Related Articles
✓ Playwright Cloud Execution BrowserStack & LambdaTest
✓ Playwright Architecture Explained
✓ Playwright Setup with TypeScript
✓ Playwright Locator Strategies
✓ Playwright Auto-Waiting Mechanism
✓ Complete Playwright Tutorial
Discover more from Rotebit
Subscribe to get the latest posts sent to your email.
