How to Fetch Content with Strapi's API with Authentication?

Are you building an application with Strapi and wondering how to properly fetch content while handling authentication? You're not alone. Many developers struggle with implementing secure authentication, especially when dealing with different types of applications and access patterns.

Whether you're building a server-side application or a frontend app with user permissions, this comprehensive guide will walk you through the authentication process step by step.

Understanding Authentication Methods in Strapi

Strapi provides two primary methods for authenticating API requests:

  1. API Tokens: Ideal for server-side applications and services where you need to fetch content programmatically without user interaction.

  2. JWT (JSON Web Tokens): Perfect for frontend applications where users need to log in and access content based on their permissions.

Let's dive into each method, starting with API tokens.

Method 1: Using API Tokens for Server Applications

API tokens are the simplest way to authenticate requests to your Strapi API when working with server-side applications. Here's how to get started:

Step 1: Generate an API Token

  1. Log into your Strapi admin panel

  2. Navigate to Settings → API Tokens

  3. Click on "Create new API Token"

  4. Fill in the following details:

    • Name: Give your token a descriptive name (e.g., "Backend Service Token")

    • Description: (Optional) Add details about the token's purpose

    • Token duration: Choose how long the token should be valid

    • Token type: Select the appropriate type based on your needs

      • Read-only

      • Full access

      • Custom

  5. Click "Save" to generate your token

⚠️ Important: Make sure to copy your token immediately after creation as you won't be able to see it again!

Step 2: Making Authenticated Requests

Once you have your API token, you can use it to make authenticated requests to your Strapi API. Here's an example using different methods:

// Using fetch
const response = await fetch('http://localhost:1337/api/articles', {
  headers: {
    'Authorization': 'Bearer your-api-token-here'
  }
});
const data = await response.json();

// Using axios
import axios from 'axios';

const response = await axios.get('http://localhost:1337/api/articles', {
  headers: {
    'Authorization': 'Bearer your-api-token-here'
  }
});
const data = response.data;

Step 3: Handling Nested Content

When fetching content that includes related data or components, you'll need to use the populate parameter. Based on common developer questions from the Strapi community, here's how to handle nested content:

// Populate all relations
const response = await axios.get('http://localhost:1337/api/articles?populate=*', {
  headers: {
    'Authorization': 'Bearer your-api-token-here'
  }
});

// Populate specific fields
const response = await axios.get('http://localhost:1337/api/articles?populate[0]=cover&populate[1]=author', {
  headers: {
    'Authorization': 'Bearer your-api-token-here'
  }
});

Step 4: Error Handling

Always implement proper error handling when making API requests:

try {
  const response = await axios.get('http://localhost:1337/api/articles', {
    headers: {
      'Authorization': 'Bearer your-api-token-here'
    }
  });
  const data = response.data;
} catch (error) {
  if (error.response) {
    // The request was made and the server responded with a status code
    // that falls out of the range of 2xx
    console.error('Error status:', error.response.status);
    console.error('Error data:', error.response.data);
  } else if (error.request) {
    // The request was made but no response was received
    console.error('No response received:', error.request);
  } else {
    // Something happened in setting up the request
    console.error('Error:', error.message);
  }
}

Method 2: Using JWT for Frontend Applications

For frontend applications where users need to log in and access content based on their permissions, you'll need to use JWT authentication. Here's how to implement it:

Step 1: User Authentication

First, you need to authenticate the user and obtain a JWT token. Here's how to implement the login process:

// Using axios
const loginUser = async (identifier, password) => {
  try {
    const response = await axios.post('http://localhost:1337/api/auth/local', {
      identifier, // This can be email or username
      password
    });
    
    // Store the JWT token
    const token = response.data.jwt;
    const user = response.data.user;
    
    return { token, user };
  } catch (error) {
    console.error('An error occurred:', error.response?.data?.message || error.message);
    throw error;
  }
};

// Usage
try {
  const { token, user } = await loginUser('user@example.com', 'password123');
  // Store token securely (see security considerations below)
} catch (error) {
  // Handle login error
}

Step 2: Making Authenticated Requests with JWT

Once you have the JWT token, you can use it to make authenticated requests:

const fetchArticles = async (token) => {
  try {
    const response = await axios.get('http://localhost:1337/api/articles', {
      headers: {
        'Authorization': `Bearer ${token}`
      }
    });
    return response.data;
  } catch (error) {
    console.error('Error fetching articles:', error);
    throw error;
  }
};

Security Considerations

When working with JWT tokens in frontend applications, consider these security best practices:

  1. Token Storage: As highlighted in this discussion, storing JWTs in localStorage can be risky. Consider using HTTP-only cookies instead:

// Example of setting up cookie storage for JWT
const setCookie = (token) => {
  document.cookie = `jwt=${token}; path=/; HttpOnly; Secure; SameSite=Strict`;
};
  1. Token Expiration: Always handle token expiration gracefully:

// Add an interceptor to handle expired tokens
axios.interceptors.response.use(
  (response) => response,
  async (error) => {
    if (error.response?.status === 401) {
      // Token expired or invalid
      // Redirect to login or refresh token
    }
    return Promise.reject(error);
  }
);

Public Access and Permission Management

Based on common developer needs, you might want to make certain collections publicly accessible without authentication. Here's how to set this up:

  1. Go to Settings → Users & Permissions Plugin → Roles

  2. Select the "Public" role

  3. Enable the appropriate permissions for your content types

  4. Save your changes

For public content, you can make requests without authentication:

// Fetching public content
const response = await axios.get('http://localhost:1337/api/public-articles');

Best Practices and Common Pitfalls

  1. Environment Variables: Always use environment variables for sensitive information:

// .env file
STRAPI_API_TOKEN=your-api-token
STRAPI_API_URL=http://localhost:1337

// Usage
const apiUrl = process.env.STRAPI_API_URL;
const apiToken = process.env.STRAPI_API_TOKEN;
  1. Error Boundaries: Implement proper error boundaries in your frontend applications:

class ErrorBoundary extends React.Component {
  state = { hasError: false };

  static getDerivedStateFromError(error) {
    return { hasError: true };
  }

  render() {
    if (this.state.hasError) {
      return <h1>Something went wrong.</h1>;
    }
    return this.props.children;
  }
}
  1. Rate Limiting: Be aware of API rate limits and implement appropriate handling:

const fetchWithRetry = async (url, options, maxRetries = 3) => {
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await axios(url, options);
    } catch (error) {
      if (error.response?.status === 429) { // Too Many Requests
        const waitTime = error.response.headers['retry-after'] * 1000;
        await new Promise(resolve => setTimeout(resolve, waitTime));
        continue;
      }
      throw error;
    }
  }
};

Conclusion

Whether you're building a server-side application using API tokens or a frontend application with JWT authentication, Strapi provides flexible and secure ways to fetch your content. Remember to:

  • Choose the appropriate authentication method for your use case

  • Implement proper security measures

  • Handle errors gracefully

  • Consider performance implications

  • Manage permissions appropriately

By following these guidelines and best practices, you'll be well-equipped to build secure and efficient applications with Strapi's API.

For more detailed information, check out the official Strapi documentation and join the active Strapi community for support and discussions.

Raymond Yeh

Raymond Yeh

Published on 21 November 2024

Get engineers' time back from marketing!

Don't let managing a blog on your site get in the way of your core product.

Wisp empowers your marketing team to create and manage content on your website without consuming more engineering hours.

Get started in few lines of codes.

Choosing a CMS
Related Posts
Choosing the Right Authentication for Your Next.js App: A Practical Guide

Choosing the Right Authentication for Your Next.js App: A Practical Guide

Choosing the right authentication for your Next.js app can be overwhelming. Explore insights on NextAuth, Clerk, and BetterAuth to find your best fit!

Read Full Story
Handling Common CORS Errors in Next.js 15

Handling Common CORS Errors in Next.js 15

Tired of seeing "Access to fetch at... has been blocked by CORS policy" errors? Dive into our guide to master CORS issues in Next.js and streamline your development process.

Read Full Story
Next.js 14 App Router: GET & POST Examples (with TypeScript)

Next.js 14 App Router: GET & POST Examples (with TypeScript)

Ready to master Next.js 14's App Router? Learn to create GET & POST route handlers with ease. Discover practical uses and advanced features using TypeScript. Start coding smarter today!

Read Full Story