When integrating Django REST Framework (DRF) with a traditional Django template, you can use JavaScript (via the Fetch API or jQuery AJAX) to call your DRF API endpoints. This guide provides step-by-step examples and covers important considerations like CSRF tokens, authentication, relevant settings/middleware, and best practices.
Overview of the Approach
In a typical setup, your Django app renders an HTML template, and that page includes JavaScript which calls DRF endpoints (usually returning JSON). If the page and the DRF API share the same domain and session, you can leverage Django’s session authentication for AJAX calls. If the API is hosted on a different domain or meant for third-party clients, you’ll likely use token-based authentication instead. In either case, you must handle CSRF protection for any “unsafe” HTTP methods (POST, PUT, PATCH, DELETE) when making requests from a Django template.
Below, we’ll walk through examples using both the Fetch API and jQuery’s AJAX, then discuss how to manage CSRF tokens and authentication.
Example: Using the Fetch API to Call a DRF Endpoint
Let’s say we have a DRF endpoint at /api/items/
that returns a JSON list of items, and we want to retrieve and display those items without a page reload. We also want to allow creating new items via a POST request. Here’s how you can do it with the Fetch API in a Django template:
-
Include CSRF Token in the Page: Ensure the page has a CSRF token available. The simplest way is to include the Django template tag
{% csrf_token %}
in your HTML (perhaps in a hidden form or as part of the page) so that Django’s CSRF cookie is set. Alternatively, use the@ensure_csrf_cookie
decorator on the view that renders the page to force setting the cookie. (If your Django template already has a form with{% csrf_token %}
, this is taken care of.) -
Get the CSRF Token in JavaScript: In your template’s script, retrieve the CSRF token value. By default, Django sets a cookie named
csrftoken
containing this value. You can read it in JavaScript with a helper function. For example:<script> function getCookie(name) { let cookieValue = null; if (document.cookie && document.cookie !== '') { const cookies = document.cookie.split(';'); for (let cookie of cookies) { cookie = cookie.trim(); if (cookie.startsWith(name + '=')) { cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); break; } } } return cookieValue; } const csrftoken = getCookie('csrftoken'); // Retrieve CSRF token from cookie </script>
The above
getCookie
function is adapted from Django’s documentation. Nowcsrftoken
holds the CSRF token string. (If you haveCSRF_COOKIE_HTTPONLY
orCSRF_USE_SESSIONS
enabled on your Django, the token won’t be in a cookie. In that case, include{% csrf_token %}
in the HTML and usedocument.querySelector('[name=csrfmiddlewaretoken]').value
to get it from the rendered form.) -
Perform a GET request with Fetch: Now you can fetch data from the DRF endpoint. For GET requests (which are “safe” methods), you technically don’t need to send the CSRF token, but it’s okay to include it out of caution (Django’s CSRF protection ignores it for safe methods). The key is to include credentials (cookies) so that the session cookie is sent along. For example:
<script> // Example: GET data from the API fetch('/api/items/', { method: 'GET', credentials: 'same-origin' // include cookies for same domain }) .then(response => response.json()) .then(data => { console.log('Fetched items:', data); // TODO: update the DOM to display items }) .catch(error => console.error('Error fetching items:', error)); </script>
In the above,
credentials: 'same-origin'
(or'include'
) ensures the browser sends the session cookie along with the request, allowing DRF’s session authentication to recognize the user. By default, if the request is same-origin, cookies will be sent, but it's good practice to be explicit. Since this is a GET request, no body is needed. -
Perform a POST request with Fetch: For creating a new item (unsafe method), we must include the CSRF token in the request header and typically send data as JSON. DRF will expect the CSRF token if we’re using session authentication. Here’s an example:
<script> // Example: POST data to the API (create a new item) const newItem = { name: "New Item", description: "Created via AJAX" }; fetch('/api/items/', { method: 'POST', credentials: 'same-origin', // include session cookie headers: { 'Content-Type': 'application/json', 'X-CSRFToken': csrftoken // include CSRF token header }, body: JSON.stringify(newItem) }) .then(response => { if (!response.ok) { throw new Error(`Error ${response.status}: ${response.statusText}`); } return response.json(); }) .then(data => { console.log('Item created:', data); // TODO: update the DOM to show the new item }) .catch(error => console.error('Error creating item:', error)); </script>
In this POST request, we set the
X-CSRFToken
header to ourcsrftoken
value. Django’s CSRF middleware looks for this header by default. We also setContent-Type: application/json
because we’re sending JSON. Thecredentials: 'same-origin'
ensures the session cookie is sent as well (needed for identifying the user session on the server side).Note: The header name must be exactly
X-CSRFToken
(case-sensitive, no extra hyphen) to be recognized by Django’s CSRF protection. Also, ensure your view or viewset for/api/items/
is not CSRF-exempt; when using SessionAuthentication, DRF will enforce CSRF for logged-in users on unsafe requests. -
Handling the Response: In the above examples, we used
.then()
to convert the response to JSON and then handle the data. For a real application, you would update the DOM (for example, append a new list item) or handle errors (perhaps show a message) accordingly. The DRF endpoint will typically return JSON data or an error status which you should be prepared to process.
This Fetch API approach can be adapted for other HTTP methods like PUT or DELETE similarly (just change method
and include the CSRF token header and body as needed). The key points are sending the CSRF token for unsafe requests and including credentials if using session-based auth.
Example: Using jQuery AJAX to Call a DRF Endpoint
If you prefer using jQuery, the process is similar. jQuery’s $.ajax
will automatically include cookies, but you still need to set the X-CSRFToken
header for unsafe methods. You can either set the header on each request or use $.ajaxSetup
to do it globally. Below is an example of a jQuery AJAX call:
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script>
// Before making AJAX calls, set up the CSRF token header for unsafe methods
$.ajaxSetup({
beforeSend: function(xhr, settings) {
if (!/^GET|HEAD|OPTIONS|TRACE$/.test(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));
}
}
});
// Example: GET request with jQuery
$.ajax({
url: "/api/items/",
type: "GET",
dataType: "json",
success: function(data) {
console.log("Fetched items (jQuery):", data);
},
error: function(xhr, status, error) {
console.error("Error fetching items:", error);
}
});
// Example: POST request with jQuery
$.ajax({
url: "/api/items/",
type: "POST",
contentType: "application/json",
data: JSON.stringify({ name: "New Item", description: "Created via jQuery" }),
headers: {
"X-CSRFToken": getCookie('csrftoken') // in case you didn't use ajaxSetup
},
success: function(data) {
console.log("Item created (jQuery):", data);
},
error: function(xhr, status, error) {
console.error("Error creating item:", xhr.responseText);
}
});
</script>
In this snippet, we first call $.ajaxSetup
to automatically add the CSRF header to any non-GET/HEAD request (when on the same domain). This uses the same getCookie('csrftoken')
function defined earlier. Then we show a GET and POST example:
-
The GET request uses
dataType: "json"
so jQuery will parse the JSON for us. It doesn’t explicitly set the CSRF header (because GET is safe and also ourajaxSetup
covers it if needed). -
The POST request sends JSON data. We set
contentType: "application/json"
and convert our data object withJSON.stringify
. We include the CSRF token header (either manually or rely on the globalbeforeSend
). Upon success, we log the returned data.
jQuery will send the session cookie automatically since the request is same-origin. If your DRF endpoint requires authentication, the session cookie (and corresponding logged-in user session) or an auth token needs to be present (more on authentication below). Also, jQuery automatically adds an X-Requested-With: XMLHttpRequest
header to AJAX requests; this isn’t required by DRF, but can be useful if you want to detect AJAX on the server side (note: request.is_ajax()
is deprecated in Django 3.1+, so usually this header is not crucial).
Handling CSRF Protection for AJAX Calls
Why CSRF matters: Cross-Site Request Forgery (CSRF) is a security mechanism to prevent malicious sites from performing actions on behalf of a logged-in user of your site. Django has CSRF protection middleware enabled by default, which requires a valid token for any state-changing request coming from a browser. This includes AJAX calls from your Django templates when using session authentication. If the CSRF token is missing or incorrect, the request will be rejected with HTTP 403 Forbidden.
How to include the CSRF token: As shown in the examples, the token can be provided via a special header X-CSRFToken
. Django’s CSRF framework will check this header on unsafe requests. The token value must match the user’s session. Usually, the token is made available as a cookie (csrftoken
) or through the {% csrf_token %}
template tag. Here are key points for managing CSRF in this context:
-
Ensure the CSRF cookie is set: Django sets the
csrftoken
cookie when it renders a template with the{% csrf_token %}
tag present. If your page has no forms and you still plan to make POST/PUT requests via JS, use the{% csrf_token %}
tag somewhere (even a hidden form) or use@ensure_csrf_cookie
on the view that renders the page. This forces the cookie to be sent. -
Read the CSRF token in JS: Use JavaScript to read the token. The earlier
getCookie('csrftoken')
function is the recommended method when the cookie is available. If you’ve configured Django to not use cookies for CSRF (CSRF_USE_SESSIONS=True
) or made the cookie HTTP-only (CSRF_COOKIE_HTTPONLY=True
), then the token must be embedded in the HTML. In that case, include the token in the page (with{% csrf_token %}
in a form) and retrieve it via the DOM:const csrftoken = document.querySelector('[name=csrfmiddlewaretoken]').value;
.
-
Send the token in AJAX requests: For any POST, PUT, PATCH, or DELETE request from your JS, add the header
X-CSRFToken: <token-value>
to the request. Both the Fetch and jQuery examples above do this. Django’s CSRF middleware will accept the token either in theX-CSRFToken
header or in a form field namedcsrfmiddlewaretoken
(the latter is what<form>{% csrf_token %}</form>
does). For AJAX, the header approach is easier. -
Verify on the server side: If the CSRF token is missing or wrong, Django will return 403. In DRF, if you’re using SessionAuthentication, you might see an error in the response like
"CSRF Failed: CSRF token missing or incorrect."
. Make sure the token you send matches what Django expects. Note that DRF’s CSRF check is slightly special: it only applies CSRF validation if the user is authenticated (to allow unauthenticated clients to call APIs without needing a token). This means if you test your AJAX without logging in, you might not get a CSRF error (because DRF skipped the check for an anonymous user), but once logged in, the CSRF protection is enforced. Always test your AJAX calls with a logged-in user if the view requires authentication. -
Don’t disable CSRF: It might be tempting to use
@csrf_exempt
on your API views to avoid dealing with the token, but this is not recommended. It opens a security hole. It’s better to correctly include the token in your requests. Only in very specific cases (like a pure API that never shares session credentials in a browser) might you consider disabling CSRF, but if you’re integrating with a Django template (thus using cookies and sessions), keep CSRF protection on for safety.
Authentication: Session-Based vs Token-Based
When calling DRF endpoints via JavaScript, authentication can happen in mainly two ways: using the user’s session (cookie-based session auth) or using tokens (like DRF Token Authentication or JSON Web Tokens). Your approach will affect how you make AJAX calls:
-
Session Authentication (Cookie-Based): This is the default when your API and front-end are part of the same Django project/domain. Once a user logs in (e.g., via Django’s
LoginView
or admin), a session cookie (sessionid
) is set. DRF’s SessionAuthentication will recognize this cookie and treat the user as authenticated for API requests. The advantage is you don’t need to manually handle tokens in JavaScript; the browser sends the session cookie automatically. However, you must include the CSRF token on any modifying requests, as described above, since session auth is vulnerable to CSRF. In our fetch/$.ajax examples, we usedcredentials: 'same-origin'
(Fetch) and relied on jQuery’s default to send cookies — that ensures the session cookie goes with the request, so the user’s identity is known on the API side. As long as the user is logged in and your AJAX calls include cookies and CSRF token, SessionAuthentication on the DRF view will work. DRF by default uses SessionAuthentication and BasicAuthentication if not overridden.Django settings: Ensure
SessionAuthentication
is enabled in DRF. By default, DRF’sDEFAULT_AUTHENTICATION_CLASSES
includes it. For example, insettings.py
you might see:REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': [ 'rest_framework.authentication.SessionAuthentication', 'rest_framework.authentication.BasicAuthentication', ], ... }
If you have customized this setting, make sure SessionAuthentication is listed to use session/cookie login for API calls. Also, the standard Django
SessionMiddleware
andAuthenticationMiddleware
should be in yourMIDDLEWARE
so thatrequest.user
is set. (These are normally included by default in new Django projects.) -
Token Authentication (DRF Token or JWT): In a token-based approach, the client (JavaScript) will include an API token in each request, typically via an
Authorization
header. DRF’s built-in TokenAuthentication expects a headerAuthorization: Token <token>
. This token is a static string (generated per user via DRF’s Token model). Alternatively, you might use JWT, which usually usesAuthorization: Bearer <jwt_token>
. In either case, the token serves to authenticate the user without sessions. When using token auth, CSRF is not required because the request is not using a session cookie. (In fact, DRF’s SessionAuthentication is what ties into CSRF; if you only use TokenAuthentication on a view, you can safely mark itcsrf_exempt
since no session is involved – though DRF might effectively not require CSRF in that case anyway.)To use DRF TokenAuthentication:
-
Include
'rest_framework.authtoken'
in yourINSTALLED_APPS
and runmanage.py migrate
to create the token model. -
In
settings.py
, add'rest_framework.authentication.TokenAuthentication'
to the default auth classes (or specifyauthentication_classes = [TokenAuthentication]
on your API views). -
Ensure clients obtain a token. DRF provides an endpoint for this (
/api-token-auth/
for exchanging username/password for a token), or you can create tokens in the admin or via signals. -
In your JavaScript, include the token in the header. For example, with Fetch:
fetch('/api/items/', { method: 'GET', headers: { 'Authorization': 'Token <your-token-here>', 'Content-Type': 'application/json' } });
And with jQuery:
$.ajax({ url: "/api/items/", headers: { 'Authorization': 'Token ' + tokenValue }, success: ... });
This will authenticate as the user who owns that token. (No
credentials: 'include'
needed, since we’re not using cookies at all in this case.)
Token auth is useful for decoupled front-ends or third-party clients. Remember that if you use token auth, you should serve your site over HTTPS, as tokens can grant access similar to a password. Also, if your front-end is a Django template (thus loaded via a Django view), you might pass the token to the template (perhaps as a JavaScript variable) after login, or have the template call a login API to retrieve a token. Some setups use session auth for web pages and token auth for a separate client; in a single-page app, you might exclusively use token auth.
-
-
Mixing Both: It’s possible to enable both SessionAuthentication and TokenAuthentication. DRF will attempt each in order. For example, you might use session auth when the user is logged in via the website, but also accept a token for AJAX calls from an external script. In practice, however, for AJAX in a Django-rendered page, you typically use the existing session (because the user is already logged in to view the page). Token auth is more common for external clients or JavaScript running on a different domain.
-
Other Authentication Schemes: DRF also supports BasicAuth, OAuth, JWT (via third-party packages), etc. Basic authentication (username/password in each request) is not user-friendly for AJAX and should only be used for testing or internal tools, since you’d have to embed credentials in the request (and it’s not CSRF-protected). OAuth/JWT are beyond this scope but follow the same concept of providing a header (and usually not using session cookies at all).
Important: If you use authentication on your API views (like IsAuthenticated
permission), ensure your JavaScript handles the case where the user is not authenticated. For example, an AJAX call might get a 401 Unauthorized or 403 Forbidden if the user’s session expired or the token is invalid. Your JS should detect this (e.g., check response.status
) and perhaps redirect to a login page or show a message. Also, if using session auth, remember that the user needs to log in via Django’s usual mechanism (you might integrate a login form or rely on the site’s login page) – once logged in, their session cookie will allow API calls.
Django Settings and Middleware Affecting AJAX–DRF Integration
When connecting Django templates and DRF via JavaScript, a few settings and middleware come into play:
-
CsrfViewMiddleware: This middleware (enabled by default in
MIDDLEWARE
) is what checks CSRF tokens on each request. It must be active for CSRF protection to work. Typically, you have it on unless you removed it. If you turned it off, you’d have to decorate views with@csrf_protect
manually (not recommended to turn it off globally). As discussed, you can use@ensure_csrf_cookie
on views rendering templates that will make AJAX posts, to ensure the cookie is set. -
SessionMiddleware: Also on by default, this enables session support. Needed for SessionAuthentication to work (and for any use of
request.session
). Keep it in the middleware. -
Authentication Middleware: The standard
AuthenticationMiddleware
associatesrequest.user
with the session. This should remain enabled so that DRF’s SessionAuthentication can seerequest.user
(although DRF actually calls Django’s authenticate again using the session key, it still relies on session). -
DRF Settings: In
settings.py
, theREST_FRAMEWORK
dictionary can specify default authentication and permission classes. For example, to use token auth only, you might set:REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': [ 'rest_framework.authentication.TokenAuthentication' ], 'DEFAULT_PERMISSION_CLASSES': [ 'rest_framework.permissions.IsAuthenticated' ], }
Adjust these based on your needs. If you expect both session and token, include both in the list. Also note, DRF’s SessionAuthentication by default enforces CSRF for logged-in users, so it works in concert with Django’s CsrfViewMiddleware.
-
CSRF Settings: Be aware of
CSRF_COOKIE_HTTPONLY
. If set to True, JavaScript cannot read the CSRF cookie directly (HTTPOnly cookies are inaccessible to JS). In such a case, as mentioned, you need to embed the CSRF token in the HTML for your JS to grab it. Similarly,CSRF_USE_SESSIONS
stores the token in the session instead of a cookie, which also means you must get it via template. In most setups, these settings remain False (the default), and using the cookie method is fine. If you enable them for security, just ensure your template strategy is adjusted (include the token in page). -
CORS (Cross-Origin Resource Sharing): If your Django template is served from the same origin as the DRF API (e.g., both from
example.com
), then you don’t have CORS issues. But if you attempt to call the API from a different domain (say your page is onmysite.com
and API onapi.mysite.com
or another port), you’ll need to handle CORS. This involves setting appropriate response headers (likeAccess-Control-Allow-Origin
) on the API. A recommended way is to use the django-cors-headers library. Install it and configureCORS_ALLOWED_ORIGINS
orCORS_ALLOW_ALL_ORIGINS
in settings. Remember to include theCorsMiddleware
. CORS is a broad topic, but essentially the browser will block cross-domain requests unless the server explicitly allows them. Also, with cross-site requests, cookies won’t be sent unless you setfetch(..., credentials: 'include')
and the server setsAccess-Control-Allow-Credentials: true
and allows the origin. If you plan to call your API from a different domain, it might be simpler to use token auth in the JS (so you don’t need cookies at all) and then just handle CORS for the token calls. -
Browsable API and Session Auth: DRF’s browsable API (the web interface at your API endpoints) uses session auth and CSRF as well. If you’re logged in to the admin or site, you can often use the browsable API. Just keep in mind, if you disable SessionAuthentication or CSRF, that might affect the browsable API experience. In general, for production, it’s fine to leave SessionAuthentication enabled alongside token auth, even if an external client uses tokens – it allows you to still use the browsable API during development with login.
-
Middleware Order: Ensure that
CsrfViewMiddleware
is placed above any view middleware that might short-circuit responses. The default order in Django’s settings should be fine. If you have custom middleware, just remember CSRF checks happen on request inCsrfViewMiddleware.process_view
. -
Debug vs Production: In debug mode, you might not notice some misconfigurations. Always test your AJAX calls in an environment that mimics production (with CSRF fully enabled). There is no special setting needed specifically for AJAX/DRF beyond what’s discussed, but good to be aware.
Best Practices for DRF and Django Template Integration
To wrap up, here are some best practices and tips when consuming DRF APIs from Django-rendered pages via JavaScript:
-
Keep it Same-Origin if Possible: If your front-end is a Django template and not a separate single-page app, use the same domain for API calls. This simplifies authentication (you can use the session) and avoids CORS issues. DRF is designed to let the AJAX use the same auth as the website.
-
Use the Appropriate Auth Mechanism: For users already logged into your site, SessionAuthentication is simplest – no extra tokens to manage in JS. For external access or if you must call from another domain, consider TokenAuthentication or JWT. Do not expose user passwords or sensitive creds in JavaScript. If using token auth, store the token securely (if in a template, you might embed it server-side for the current user or fetch it via an AJAX login flow). And always use HTTPS for tokens.
-
Always Include CSRF Token for Unsafe Methods: This cannot be emphasized enough. It’s easy to forget. A common pattern is to write a small JavaScript utility that automatically attaches
X-CSRFToken
to AJAX calls (like the jQuerybeforeSend
setup above, or even using a global fetch wrapper). This ensures you don’t accidentally send a POST without a token. Django’s docs explicitly recommend setting theX-CSRFToken
header on each XMLHttpRequest. This will save you from mysterious 403 errors. -
Structure Your Code for Clarity: Write your JavaScript in a way that’s maintainable. You might keep your AJAX logic in a separate
.js
file and include it in the template. If you have multiple API endpoints, consider organizing functions (e.g.,function loadItems(){ fetch(...); }
). Also handle UI state (loading indicators, disabling buttons while awaiting response, etc.) to improve UX. -
Handle Errors and Authentication States: If an API call returns 401 Unauthorized, you might need to redirect the user to log in. If it returns 403 CSRF error, you should check your token setup. For 500 errors or others, at least inform the user that something went wrong. Also consider cases where the network fails – wrap fetch in try/catch (or
.catch
) and maybe retry or show a message. -
Test with Different Scenarios: Test your integration with a logged-in user, a logged-out user, and (if applicable) with an invalid/expired token. This will ensure your logic for authentication and CSRF holds up. As noted, DRF will let unauthenticated requests through without CSRF check, so testing only while not logged in might hide a problem.
-
Avoid Duplicate Logic: If you find yourself writing a Django view that renders data that’s essentially available via a DRF API, you might be duplicating effort. Consider using the DRF API for the data and just use Django views for the page shell. For example, your template loads and then JS calls the API for data. This way, your data serialization is all in the DRF serializers. This is a common pattern for partial page updates. However, for initial page load, you might still fetch some data on the server side for SEO or faster render – it’s a trade-off.
-
Security: Keep security at the forefront. Besides CSRF, ensure that your API endpoints have proper permissions (e.g., use DRF’s
IsAuthenticated
or custom perms as needed) so that sensitive data isn’t exposed via AJAX to unauthorized users. Also, when using tokens, protect them. Do not store tokens in plain JavaScript variables for long term or in localStorage without understanding the security implications (XSS can steal them). If the AJAX pages are only accessible to logged-in users, it may be fine to embed a token in the HTML (since an XSS could grab either the session cookie or token anyway). Just be mindful of where you put credentials. -
Use Official Documentation: When in doubt, refer to Django and DRF docs. Django’s CSRF docs have a section on AJAX that we followed here, and DRF’s documentation on AJAX/CSRF is also useful. These resources are kept up-to-date with best practices.
By following the above guidance, you can successfully integrate dynamic JavaScript calls into a Django template-driven app, backed by the powerful Django REST Framework. You’ll get the best of both worlds: the ease of Django templates for structure and the flexibility of a client-side script for interactivity, all while using DRF to provide a clean API layer.
Sources:
-
Django Documentation – How to use Django’s CSRF protection (explains retrieving CSRF tokens and AJAX)
-
Django Documentation – CSRF protection edge cases (on ensuring the CSRF cookie is set for AJAX)
-
Django REST Framework Docs – AJAX, CSRF & CORS (discusses using Session vs Token auth for AJAX and CSRF needs)
-
Django REST Framework Docs – Authentication (session auth with CSRF vs token auth, and CSRF enforcement details)
-
Django REST Framework Docs – Token Authentication (using Authorization header with token)
-
TestDriven.io – Working with AJAX in Django (illustrates fetch and jQuery examples with CSRF)
0 Comments