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:
API Tokens: Ideal for server-side applications and services where you need to fetch content programmatically without user interaction.
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
Log into your Strapi admin panel
Navigate to Settings → API Tokens
Click on "Create new API Token"
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
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:
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`;
};
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:
Go to Settings → Users & Permissions Plugin → Roles
Select the "Public" role
Enable the appropriate permissions for your content types
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
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;
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;
}
}
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.