cy.request() is Cypress’s built-in HTTP client. It supports all standard HTTP methods (GET, POST, PUT, PATCH, DELETE), custom headers, JSON and form-encoded bodies, query parameters, and automatic cookie handling. Unlike external HTTP clients, cy.request() integrates directly into the Cypress command chain — responses are yielded to .then() callbacks and can be asserted with .its() and .should().
cy.request — Complete HTTP Method Reference
Every HTTP method follows the same pattern: specify the method, URL, options, then assert on the response.
// ── GET request ──
cy.request('GET', '/api/products').then((response) => {
expect(response.status).to.eq(200);
expect(response.headers['content-type']).to.include('application/json');
expect(response.body).to.be.an('array');
expect(response.body).to.have.length.greaterThan(0);
expect(response.duration).to.be.lessThan(2000); // Response time < 2s
});
// Shorthand — GET is the default method
cy.request('/api/products').its('status').should('eq', 200);
// ── POST request with JSON body ──
cy.request({
method: 'POST',
url: '/api/users',
body: {
name: 'Alice Tester',
email: 'alice@test.com',
role: 'tester',
},
headers: {
'Content-Type': 'application/json',
},
}).then((response) => {
expect(response.status).to.eq(201);
expect(response.body).to.have.property('id');
expect(response.body.name).to.eq('Alice Tester');
expect(response.body.email).to.eq('alice@test.com');
});
// ── PUT request (full update) ──
cy.request({
method: 'PUT',
url: '/api/users/42',
body: {
name: 'Alice Updated',
email: 'alice.updated@test.com',
role: 'senior-tester',
},
}).then((response) => {
expect(response.status).to.eq(200);
expect(response.body.name).to.eq('Alice Updated');
});
// ── PATCH request (partial update) ──
cy.request({
method: 'PATCH',
url: '/api/users/42',
body: { role: 'lead-tester' },
}).then((response) => {
expect(response.status).to.eq(200);
expect(response.body.role).to.eq('lead-tester');
// Name and email should remain unchanged
expect(response.body.name).to.eq('Alice Updated');
});
// ── DELETE request ──
cy.request({
method: 'DELETE',
url: '/api/users/42',
}).then((response) => {
expect(response.status).to.eq(204); // No Content
});
// Verify deletion
cy.request({
method: 'GET',
url: '/api/users/42',
failOnStatusCode: false, // Prevent Cypress from failing on 404
}).then((response) => {
expect(response.status).to.eq(404);
});
// ── Query parameters ──
cy.request({
url: '/api/products',
qs: {
category: 'electronics',
sort: 'price_asc',
limit: 10,
},
}).then((response) => {
// Request sent as: /api/products?category=electronics&sort=price_asc&limit=10
expect(response.body).to.have.length.at.most(10);
});
// ── Response assertions — comprehensive validation ──
cy.request('/api/products/1').then((response) => {
// Status code
expect(response.status).to.eq(200);
// Response headers
expect(response.headers).to.have.property('content-type');
expect(response.headers['content-type']).to.include('application/json');
// Response body structure
expect(response.body).to.have.all.keys('id', 'name', 'price', 'description');
// Data types
expect(response.body.id).to.be.a('number');
expect(response.body.name).to.be.a('string').and.not.be.empty;
expect(response.body.price).to.be.a('number').and.be.greaterThan(0);
// Response time
expect(response.duration).to.be.lessThan(1000);
});
// ── Concise chained assertions ──
cy.request('/api/health')
.its('status').should('eq', 200);
cy.request('/api/products')
.its('body')
.should('be.an', 'array')
.and('have.length.greaterThan', 0);
cy.request('/api/products')
.its('body.0.name') // First product's name
.should('be.a', 'string');
cy.request() fails the test if the response status code is not in the 2xx or 3xx range. This is usually what you want — a 500 error should fail your test. But when you are testing error responses (404 for deleted resources, 422 for validation errors, 401 for unauthorized access), you must set failOnStatusCode: false to prevent Cypress from treating the error response as a test failure. Without this option, your negative API tests will fail before you can assert on the error response..its('body.0.name') as a shorthand for accessing nested response properties. The .its() command supports dot notation to traverse objects: .its('response.body.data.users.0.email') reaches deeply nested values without multiple .then() callbacks. Combined with .should(), this creates one-line API assertions: cy.request('/api/me').its('body.email').should('eq', 'alice@test.com').cy.request() automatically sends cookies from the current browser session. If you have logged in via cy.visit() and the server set an authentication cookie, subsequent cy.request() calls include that cookie automatically. This is convenient for authenticated API calls but can cause confusion in tests that expect unauthenticated behaviour — the request secretly includes a valid session cookie. Use cy.clearCookies() before making unauthenticated API test requests.Common Mistakes
Mistake 1 — Not setting failOnStatusCode: false for error response tests
❌ Wrong: cy.request('GET', '/api/users/99999') — Cypress fails the test with “cy.request() failed: 404 Not Found” before you can assert on the 404.
✅ Correct: cy.request({ url: '/api/users/99999', failOnStatusCode: false }).its('status').should('eq', 404)
Mistake 2 — Not validating response body structure, only status codes
❌ Wrong: cy.request('/api/products').its('status').should('eq', 200) — only verifies the server responded, not that the response contains valid data.
✅ Correct: Asserting status AND body structure, data types, and key values to ensure the API contract is fulfilled.