Simple Requests in Frontend Development

Serhii Koziy
3 min readJan 23, 2025

As a Senior Frontend Developer, working with APIs is part and parcel of daily life. When dealing with cross-origin requests, understanding the concept of a “simple request” is essential. This article will explore what a simple request is, its criteria, and provide examples using TypeScript.

What is a Simple Request?

In the context of Cross-Origin Resource Sharing (CORS), a simple request is a type of HTTP request that adheres to specific conditions laid out by the CORS specification. Simple requests are designed to be straightforward and avoid the need for a preflight request — an additional request made by the browser to check permissions before sending the actual request.

Preflight requests add overhead, so simple requests can be more efficient when your request meets the necessary criteria.

Criteria for a Simple Request

To qualify as a simple request, three key conditions must be met:

  1. HTTP Methods

The request must use one of the following HTTP methods:

  • GET
  • POST
  • HEAD

2. Headers

  • Only certain headers are allowed in the request. These are known as the CORS-safelisted request headers and include:
  • Accept
  • Accept-Language
  • Content-Language
  • Content-Type (with additional restrictions; see below)

3. Content-Type Restrictions

  • If the request includes a Content-Type header, its value must be one of the following:
  • text/plain
  • application/x-www-form-urlencoded
  • multipart/form-data

4. No Custom Headers

  • The request must not include any custom headers beyond those specified above.

5. No Credentials (Optional)

  • If credentials such as cookies, HTTP authentication, or client-side SSL certificates are included, they must be explicitly allowed by the server with the Access-Control-Allow-Credentials header.

If these criteria are met, the browser will send the request directly without initiating a preflight.

Simple Request Example with TypeScript

Let’s see how this works in practice using TypeScript.

Example 1: Making a GET Request

async function fetchUserData(userId: string): Promise<any> {
const response = await fetch(`https://example.com/api/users/${userId}`, {
method: 'GET',
headers: {
'Accept': 'application/json',
},
});

if (!response.ok) {
throw new Error(`Error fetching user data: ${response.statusText}`);
}
return response.json();
}

fetchUserData('123')
.then(userData => console.log(userData))
.catch(err => console.error(err));

This is a simple request because:

  • It uses the GET method.
  • The Accept header is a safelisted header.
  • No custom headers are added.

Example 2: Making a POST Request with application/x-www-form-urlencoded

async function submitForm(data: { name: string; email: string }): Promise<any> {
const formBody = new URLSearchParams();
formBody.append('name', data.name);
formBody.append('email', data.email);

const response = await fetch('https://example.com/api/submit', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: formBody.toString(),
});
if (!response.ok) {
throw new Error(`Error submitting form: ${response.statusText}`);
}
return response.json();
}

submitForm({ name: 'John Doe', email: 'john.doe@example.com' })
.then(response => console.log(response))
.catch(err => console.error(err));

This is a simple request because:

  • It uses the POST method.
  • The Content-Type is application/x-www-form-urlencoded, which is safelisted.
  • No custom headers are added.

When Does a Request Fail to Qualify as Simple?

Any deviation from the criteria will result in a request being considered “non-simple,” triggering a preflight request. For example:

  1. Using a method like PUT or DELETE.
  2. Adding custom headers such as Authorization.
  3. Using a Content-Type like application/json (this is NOT safelisted).

Here’s an example of a non-simple request:

async function updateUser(userId: string, data: { name: string; email: string }): Promise<any> {
const response = await fetch(`https://example.com/api/users/${userId}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer <token>'
},
body: JSON.stringify(data),
});

if (!response.ok) {
throw new Error(`Error updating user: ${response.statusText}`);
}
return response.json();
}

updateUser('123', { name: 'Jane Doe', email: 'jane.doe@example.com' })
.then(response => console.log(response))
.catch(err => console.error(err));

This request will trigger a preflight because:

  • The method PUT is not allowed for simple requests.
  • The Content-Type header is not safelisted.
  • A custom Authorization header is included.

Conclusion

Simple requests are an efficient way to interact with APIs while avoiding the overhead of preflight requests. By understanding their criteria and tailoring your frontend code accordingly, you can optimize your application’s performance and reduce unnecessary complexity.

Whenever possible, structure your requests to qualify as simple — especially for high-frequency operations such as fetching data or submitting forms. And, as always, ensure that your server’s CORS configuration aligns with your application’s requirements.

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

No responses yet

Write a response