Next.js: Getting URL origin as prop from getInitialProps (server and client)

Created on 11 Feb 2019  路  14Comments  路  Source: vercel/next.js

Feature request

Is your feature request related to a problem? Please describe.

Next.js version: 8
Now version: 2

As a user, this problem occurs when I'm using next/link to navigate to a page that is using .getInitialProps, in which data is being fetched from a Now deployment URL. To get the absolute URL for my data fetch and SSR, I check req.headers.host, which is fine for SSR. However, next/link makes, .getInitialProps run in the client, so there is no req object and I have to check window.location.hostname instead.

This makes getting the absolute URL for my Now deployment look a bit messy, and I think this could be handled by the framework rather than the user. It looks like this:

Page.getInitialProps = async ({ req }) => {
  let protocol = 'https:'
  let host = req ? req.headers.host : window.location.hostname
  if (host.indexOf('localhost') > -1) {
    protocol = 'http:'
  }

  const jobs = await (
    await fetch(`${protocol}//${host}/api/allJobs.js`)
  ).json()

  return {
    jobs
  }
}

Describe the solution you'd like

An additional origin prop passed to .getInitialProps which contains the protocol + hostname, regardless if on the server or client:

Page.getInitialProps = async ({ origin }) => {
  const job = await (
    await fetch(`${origin}/api/job.js`)
  ).json()
  return {
    jobs
  }
}

Describe alternatives you've considered

My current solution is to an additional function to check if req is an object, and if not, get the hostname from window.location.hostname, which looks like this:

import absoluteUrl from 'next-absolute-url'

Page.getInitialProps = ({ req }) => {
  const { protocol, host } = absoluteUrl(req)
  const apiURL = `${protocol}//${host}`
}

Where next-absolute-url is a function that does this:

function absoluteUrl (req, setLocalhost) {
  var protocol = 'https:'
  var host = req ? req.headers.host : window.location.hostname
  if (host.indexOf('localhost') > -1) {
    if (setLocalhost) host = setLocalhost
    protocol = 'http:'
  }

  return {
    protocol: protocol,
    host: host
  }
}

module.exports = absoluteUrl

Additional context

spectrum.chat thread in which I ran into the issue

Most helpful comment

Hey @scottie-schneider :wave:
I'm not sure if getting the URL origin was ever implemented since closing the issue and I've just been using my userland module, next-absolute-url, and it seems to work enough for me to not look much farther.

Here is an example of how I've used it recently:

Page.getInitialProps = async ({ req, query }) => {
  const { protocol, host } = absoluteUrl(req)
  const yelp = `${protocol}//${host}/api/yelp.js`
  const body = JSON.stringify({
    search: query.pid.replace(/-/g, ' ')
  })
  const payload = await fetch(yelp, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body
  })
  const data = await payload.json()
  return data
}

I do use this module in every Next.js project now, I'm curious about how others handle getting the URL origin.

Additionally, I should probably update (or accept a PR) to make next-absolute-url also return the origin so the weird ${protocol}//${host}/api/job.js string interpolation thing I documented could instead be ${origin}/api/job.js.

All 14 comments

I don't think we should make the Javascript bundle bigger by introducing this feature when it can be handled as-needed in userland 馃

Gotchya, there's probably a nicer way to handle this in userland than what I'm doing right now. I'll put some more thought into it. I wonder if _app.js is a better place to handle passing the URL down to getInitialProps

Sure, you would be able to do that in _app.js

Cool! I'll try that out in the future. Thanks @timneutkens !

Closing the thread as I don't think it needs to stay open.

:100: 馃檹 Thanks for understanding!

@jekrb Did you ever figure out a nicer way to handle?

Hey @scottie-schneider :wave:
I'm not sure if getting the URL origin was ever implemented since closing the issue and I've just been using my userland module, next-absolute-url, and it seems to work enough for me to not look much farther.

Here is an example of how I've used it recently:

Page.getInitialProps = async ({ req, query }) => {
  const { protocol, host } = absoluteUrl(req)
  const yelp = `${protocol}//${host}/api/yelp.js`
  const body = JSON.stringify({
    search: query.pid.replace(/-/g, ' ')
  })
  const payload = await fetch(yelp, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body
  })
  const data = await payload.json()
  return data
}

I do use this module in every Next.js project now, I'm curious about how others handle getting the URL origin.

Additionally, I should probably update (or accept a PR) to make next-absolute-url also return the origin so the weird ${protocol}//${host}/api/job.js string interpolation thing I documented could instead be ${origin}/api/job.js.

@jekrb , I really can't thank you enough. Your module has enabled me to breakthrough after a few frustrating hours of trying to get the baseURL nicely in Next.js. Thank you!

Just a note for those who are coming from google, the @jekrb's answer is right but the host is renamed to hostname

Here is what location has i nit locally for me:
host: "localhost:3000"
hostname: "localhost"

So I needed to use this in the absoluteUrl function:
var host = req ? req.headers.host : window.location.host
Otherwise I don't get the port number in the api url.
I was getting:
http://localhost/api/dbtest
instead of
http://localhost:3000/api/dbtest

@stokescomp what version of next-absolute-url are you using?

I recently merged a pull request that rewrote everything in typescript and added tests. If you haven't yet, an upgrade might help fix the error that you're running into. If you're on the latest version, feel free to open an issue on the project repo and I'll see if I can help!

I was copying an old version of your function. Now I added import absoluteUrl from "next-absolute-url"; and added the npm module to package.json and it works great now. I love your improvements to it. Thanks for sharing!

And it works perfectly with no errors.

absoluteUrl from "next-absolute-url"; would not wotk, if you are accessing with loopback(127.0.0.1) or with local ips. i rather prefer to check wether my environment is development or production. If it is development use "http", otherwise i use "https"

@manan5439 there are a couple issues open about that:

https://github.com/jekrb/next-absolute-url/issues/12
https://github.com/jekrb/next-absolute-url/issues/14

I don't currently have the time to work on an update, but would be more than happy to accept a pull request 馃憤

Was this page helpful?
0 / 5 - 0 ratings