How to make Django Server URL config-driven instead of being hard-coded? - django

I am using Django with React, each time I have to use React to get the data from the database I have to do something like this:
var URL = 'http://127.0.0.1:8000/users/api/games/'
var API_URL = URL + '?term=' + this.props.token
await axios.get(API_URL)
.then(res => {
temp = res.data
})
How can I avoid hardcoding "http://127.0.0.1:8000/" like this and set up it in the setting.py? Can someone show me how to do it? Thanks a lot!

If I understand you correctly this only needs to be changed in the React app and not in the settings.py of Django.
Take the URL of wherever you deploy your Django application (in development this could be locally at 127.0.0.1:8000 but in Production this will most likely be something like api.example.com) and this will become an Environment Variable injected at build time of your React app.
Generally you will have a .env configuration file per environment (DEV/TEST/PROD etc.) which will contain all variables that can then be used in your code.
Check out Using environment variables in React for a detailed post on why you would use environment variables and how to use them in your Javascript applications.

Related

Problems with AWS Amplify, Next.js and authenticated SSR

I've got a Next.js application that uses AWS Cognito userpools for authentication. I have a custom UI and am using the aws-amplify package directly invoking signIn/signOut/etc... in my code. (I previously used the AWS Hosted UI and had the same problem set out below - I hoped switching and digging into the actual APIs who reveal my problem but it hasn't)
Everything in development (running on localhost) is working correctly - I'm able to login and get access to my current session both in a page's render function using
import { Auth } from 'aws-amplify';
...
export default const MyPage = (props) => {
useEffect(async () => {
const session = await Auth.currentSession();
...
}
...
}
and during SSR
import { withSSRContext } from 'aws-amplify';
...
export async function getServerSideProps(context) {
...
const SSR = withSSRContext(context);
const session = await SSR.Auth.currentSession();
...
}
However, when I deploy to AWS Amplify where I run my staging environment, the call to get the current session during SSR fails. This results in the page rendering as if the user is not logged in then switching when the client is able to determine that the user is in fact logged in.
Current Hypothesis - missing cookies(??):
I've checked that during the login process that the AWS cookies are being set correctly in the browser. I've also checked and devtools tells me the cookies are correctly being sent to the server with the request.
However, if I log out context.req.headers inside getServerSideProps in my staging environment, the cookie header is missing (whereas in my dev environment it appears correctly). If this is true, this would explain what I'm seeing as getServerSideProps isn't seeing my auth tokens, etc... but I can't see why the cookie headers would be stripped?
Has anyone seen anything like this before? Is this even possible? If so, why would this happen? I assume I'm missing something, e.g. config related, but I feel like I've followed the docs pretty closely - my current conf looks like this
Amplify.configure({
Auth: {...}
ssr: true
});
Next.js version is 11.1.2 (latest)
Any help very much appreciated!
You have to use Next#11.0.0 to use getServerSideProps, withSSRContext and Auth module in production.
I had same issue.
My solution was that disconnect a branch has an authentication problem once and reconnect the branch.
What are your build settings? I guess you are using next build && next export in which case this getServerSideProps shall not work. See https://nextjs.org/docs/advanced-features/static-html-export#unsupported-features
To use SSR with AWS amplify see https://docs.aws.amazon.com/amplify/latest/userguide/server-side-rendering-amplify.html#redeploy-ssg-to-ssr or consider deploying on a node server that is actually a server that you can start with next start like AWS EC2 or deploy on Vercel.
Otherwise if you use next export have to make do with client side data fetch only with client side updates only and cannot use dynamic server side features of nextjs.
One reason for context.req.headers not having any cookie in it is because CloudFront distribution is not forwarding any cookies.
This “CloudFront Behaviour” can be changed in two ways:
Forward all cookies, OR
Forward specified cookies (i.e. array of cookie names)
To change the behaviour, navigate to CloudFront on AWS console > Distributions > your_distribution > Behaviors Tab.
Then Edit existing or Create new behaviour > Change cookies settings (for example set it to "All")

How to link Django and React URLs to perform actions?

In Django, I have my login URL set to 'api/auth/login'. When given a username and password, it will log that user in. Running 'python manage.py runserver', it will put that URL at 'http://127.0.0.1:8000/api/auth/login'
However, my React project, when running 'yarn start' is at 'http://localhost:3000/' and giving it the extension 'api/auth/login', the url it attempts is 'http://localhost:3000/api/auth/login'.
This does not work, but if I replace the URL extension with 'http://127.0.0.1:8000/api/auth/login', the login works as expected.
How can I have the URLs work with my React app? Or is this not something that necessarily needs to be fixed? I do plan to host this project somewhere and am not yet sure how the URLs will work out..
One option is to set proxy in the package.json.
Second option is to set axois baseURL:
// add in your code before main component definition
// for example in App.js
import axios from "axios";
axios.defaults.baseURL = "http://127.0.0.1:8000";
I'm preferring the second approach. For doing production build it can be easily overwritten with an environment variable.
import axios from "axios";
axios.defaults.baseURL = REACT_APP_SERVER_URL
Remember to set the CORS headers in the Django server (I'm working on tutorial with an example how to do this).
You are hosting react application on another port that's why when you put a request without mentioning host, it gets appended to your current host i.e. 127.0.0.1:3000. I suggest you write "proxy": '127. 0.0.1:8000' in your package.json (refer https://create-react-app.dev/docs/proxying-api-requests-in-development/) and restart your react server or use full url of django server i.e. 127.0.0.1:8000/

React Django REST framework session is not persisting/working

I'm working on a project with Django Rest Framework as back-end and React as front-end. When I set a session variable initially in some function/view and later when I try to access the different view through axios call and in that view if I try to access session variable which i created previously, I get KeyError. Session doesn't seem stored.
I googled I got a similar issue which I'm facing.
Django rest framework Reactjs sessions not working
I followed the process by adding { withCredentials: true } in axios call. Now I'm getting a different error. Now the issue is not able to access the backend. I get an error saying Access to XMLHttpRequest at 'http://127.0.0.1:8000/url/' from origin 'http://localhost:3000' has been blocked by CORS policy
Again I googled the issue which I'm getting and found that I've to add CORS_ORIGIN_WHITELIST in the django settings.py
I followed the below post for that
Django Python rest framework, No 'Access-Control-Allow-Origin' header is present on the requested resource in chrome, works in firefox
I have added CORS_ORIGIN_WHITELIST like this
CORS_ORIGIN_WHITELIST = [
'http://localhost:3000',
'http://127.0.0.1:3000'
]
Still, I'm facing the same issue. I don't know what's going wrong.
Finally after so much of research I found a solution for this.
In the file where we are importing axios to make the call, set the default header below your import
axios.defaults.withCredentials = true;
example:
import axios from "axios";
axios.defaults.withCredentials = true;
axios.get("url")
.then(response => {
console.log(response)
})
.catch(error => {
console.log(error);
});
once this is done go to your settings.py file and add the below configuration
CORS_ORIGIN_ALLOW_ALL = True
CORS_ALLOW_CREDENTIALS = True
so after this if you set a session variable and access it later in any view, you would be able to get the value which you had previously stored.
This solution worked for me. Hopefully if anyone has the same issue, it will work for them too. :)
NOTE
If the session is not storing under localhost:3000 then make sure that you're accessing your front-end application through 127.0.0.1:3000. If you access the front-end application through localhost and if your backend is running on 127.0.0.1 then the session cookie will be set to 127.0.0.1, so changing the front-end URL from localhost:3000 to 127.0.0.1:3000 will solve your problem.
I had a similar issue once. I used firefox as a browser and got the problem solved by installing the CORS everywhere plugin for firefox (https://addons.mozilla.org/en-US/firefox/addon/cors-everywhere/). So, if you're using firefox, this might solve your problem.

React production error with service worker: invalid MIME type

I have a React app that works in development and in production, however in production I get the following error in the console:
The script has an unsupported MIME type ('text/html').
Failed to load resource: net::ERR_INSECURE_RESPONSE
registerServiceWorker.js:80 Error during service worker registration:
DOMException: Failed to register a ServiceWorker: The script has an unsupported MIME type ('text/html').
The error does not happen in development, only in the production environment. The app still works correctly in production, however I would still prefer to sort out the error.
After doing some digging, it seems that in production the service-worker.js file is requested from the original index.html file while has a MIME type of text/html, the service-worker.js file therefore does not have the correct MIME type which would need to be application/javascript.
Unfortunately even though I think I understand what the issue is, I haven't been able to fix it.
The production build was created using create-react-app and is served up by a Django backend. The index.html page containing the React app is served up as follows:
re_path('.*', TemplateView.as_view(template_name='index.html'))
Is there perhaps something on Nginx that needs changing? I would guess not since I have other production sites working correctly with respect to MIME types (however none of them are React apps requiring service workers).
It happens if you have registered some service workers. If you open Chrome developer tools you may get some errors.
Go to
Chrome Dev Tools -> Application -> Service Worker
In there if you are seeing some service worker is registered then you have to unregister it by clicking it and after that refresh the page and check.
From registrationService, I can see that:
export default function register() {
if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
const publicUrl = new URL(process.env.PUBLIC_URL, window.location);
if (publicUrl.origin !== window.location.origin) {
return;
}
window.addEventListener('load', () => {
const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
if (isLocalhost) {
checkValidServiceWorker(swUrl);
} else {
registerValidSW(swUrl);
}
});
}
}
When its production environment, then it tries to register to registerValidSW(swUrl); where swUrl is <your domain>/service-worker.js(FYI PUBLIC_URL is set from homepage value of package.json). But I am guessing that is not a valid path, so its getting index.html from Django. That might be why you are getting this error.
Again, based on guess, I think you can do the following solution:
Serve service-worker.js in from static folder (for example url could be: /static/service-worker.js).
Update in registrationService.js: ${process.env.PUBLIC_URL}/service-worker.js to ${process.env.PUBLIC_URL}/static/service-worker.js.
See how it goes. There is another solution if you don't want to use registerService in production, just not to register that service(I am not sure if its recommended or not, but its up to you)
if (process.env.NODE_ENV != 'production') {
registerServiceWorker();
}

Change Flask config after app creation

I am using Livereload + Flask + Flask SOcketIO + Flask Script.
The problem is that SocketIO needs a gevent-socketio server. Which is all livereload is not. Do not get me wrong, I do not want to solve this issue. I only run livereload to do some css/sass/js work so it's ok.
But the exception throwed at me every time the client tried to connect to the socketIO server bothers me a lot. So I thought I could just disable javascript, but my app relies mainly on angular to asynchronously render directives, so there would be no tags on the page for me to be able to switch styles. Then I realize I could write a config property so when I load the page Jinja2 would provide it as a javascript variable and I would not load the specific socket-io js file in case it is set to False.I have tried this.
Setting it on config.py:
class Config:
LIVERELOAD = False
I run the livereload server with FLask-Script
class liveReloadServer(Command):
"""prints hello world"""
def run(self):
with app.app_context():
server = Server(app.wsgi_app)
Config.LIVERELOAD = 'True'
server.serve(port=8080)
Passing the config property to javascript side
var LIVERELOAD = "{{ config['LIVERELOAD'] }}";
But this does not work ( LIVERELOAD always show True) cause I am setting the property after the Config class has already been inserted into the Flask app object.Is there another way?
You need to set the Flask app's config, which is not tied to any custom objects you created.
app.config['LIVERELOAD'] = True