Home > database >  Using nginx subdomains as a React parameter
Using nginx subdomains as a React parameter

Time:12-08

As the title asks, is there a recommended method for using:

username.domain.com/event/id/

Where I can ask React to reference both id and username as parameters?

As of now

Currently I can obtain id fine using React router (or any parameter after the domain name in the url).

App.js

import { BrowserRouter as Router, Route, Routes } from "react-router-dom";


<Router>
    <Routes>
        <Route path="/event/:id/"
            element={<>
                <EventComponent />
            </>}
        />
    </Routes>
</Router>

EventComponent.jsx

import { useParams } from 'react-router-dom';
let params = useParams();
let eventID = params.id;
// I can now use eventID to reference what was in the url

I can catch server subdomains in my nginx.conf. This is what I use to reach my React app (not a wildcard subdomain yet, but that's not much of a change and not the problem):

server {

    listen      443 ssl;
    charset utf-8;
    client_max_body_size 10M;

    server_name         domain.com;
    
    root                "/usr/share/nginx/html/domain.com/public_html";

    location / {
        index            index.html;
        try_files $uri $uri/ /index.html?/$request_uri;
    }

    location /sw.js {
        add_header Cache-Control "no-cache";
        proxy_cache_bypass $http_pragma;
        proxy_cache_revalidate on;
        expires off;
        access_log off;
    }
    
}

Problem

But I don't know a clean way to pass subdomains to React without rewriting the url in nginx so that the subdomain is becomes part of the url after the domain. This is not a scenario that's desired.

Relevant criteria

As mentioned, I don't want to rewrite the domain from the above url into something like domain.com/event/id/username/

The structure of the url and presenting it as the original, to the user, is important.

Secondly, the subdomain is a wildcard. It won't be a fixed string. I can process this fine in nginx but it's important that a solution allows for a dynamic string.

CodePudding user response:

A subdomain is something only the Webserver should handle.
React router is not able to distinguish between them.

There are multiple ways of achieving the desired outcome, I'll share my thoughts based on previous experiences.

Note: This assumes all the subdomains should point to the React index.html


Query Parameter

(I've used this in production, works great)
You can let Nginx add a query parameter with the subdomain (../?user=foobar), this can easily be parsed with Javascript, to ensure the user does not notice this, you can remove the param after the page loads, the user won't even notice

server_name  ~^(?<subdomain>. )\.example\.com$; 
return       301 http://example.com$request_uri?user=$subdomain;

Cookie

The same idea as above, but instead off using query parameters, you can inject the subdomain into a cookie and read it using Javascript

Native Props

Since all the subdomains will trigger the same React app, we can use the regular Javascript way of retrieving the subdomain, this can be passed to deeper components as a normal prop.

# App.js
const subdomain = // prefered way of retreiving subdomain;
<EventComponent subdomain={subdomain} />

# EventComponent
let params = useParams();
let eventID = params.id,
    subdomn = props.subdomain

CodePudding user response:

If username.domain.com is the public url, I don't quite see why you have to pass this to react, surely you can just interrogate the domain name from the browser directly.

If not, please add info with an example of the public and local (to nginx) domains and maybe elaborate on some of the reverse proxy rules and I'll remove the answer / attempt to update.

EventComponent.jsx

import { useParams } from 'react-router-dom'
const params = useParams()
const eventID = params.id
const username = window.location.hostname.split('.')[0]

Working example of using this method in React:

const Link = ReactRouterDOM.Link
const Route = ReactRouterDOM.Route

class ArticleComponent extends React.Component {
  constructor(props) {
    super(props)
    this.hostname = window.location.hostname
    this.username = window.location.hostname.split('.')[0]
  }
  render() {
    return (
      <div>
        <p>Username fetched from parent App and passed as prop in the standard React manner</p>
        <p><i>Note: It will say `stacksnippets` as the username, because there are only two levels in the hostname. If there were 3 levels eg, `user1.domain.com`, the username would be `user1`</i></p>
        <p>Hostname: <b>{this.hostname}</b></p>
        <p>Username via props: <b>{this.props.username}</b></p>
        <p>Username from component directly: <b>{this.username}</b></p>
        <p>Article ID: <b>{this.props.match.params.id}</b></p>
      </div>
    )
  }
}

class App extends React.Component {
  constructor(props) {
    super(props)
    this.props.username = window.location.hostname.split('.')[0]
  }
  render() {
    return (
      <ReactRouterDOM.HashRouter>
        <ul>
          <li><Link to="/">TO HOME</Link></li>
          <li><Link to="/articles/link1">TO /articles/link1</Link></li>
          <li><Link to="/articles/link2">TO /articles/link2</Link></li>
        </ul>
        <Route path="/" exact component={Home} />
        <Route path="/articles/:id" render={(props) => (<ArticleComponent username={this.props.username} {...props}/>)} />
      </ReactRouterDOM.HashRouter>
    )
  }
}

const Home = props => <div><h1>HOME</h1><p>Click a link to navigate</p></div>

ReactDOM.render(<App />, document.querySelector('#root'))
<script src='https://unpkg.com/[email protected]/umd/react.production.min.js'></script>
<script src='https://unpkg.com/[email protected]/umd/react-dom.production.min.js'></script>
<script src='https://unpkg.com/[email protected]/umd/react-router-dom.min.js'></script>
<script src='https://unpkg.com/[email protected]/babel.js'></script>
<div id='root'></div>

  • Related