Skip to content

Commit d60baeb

Browse files
authored
Add support for calling a separate api host in fetchFromAPI based on … (#54)
* Add support for calling a separate api host in fetchFromAPI based on an API_HOST environment variable. * bump version to 7.7.0
1 parent 7b930ba commit d60baeb

File tree

5 files changed

+89
-52
lines changed

5 files changed

+89
-52
lines changed

package-lock.json

Lines changed: 8 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "react-storefront",
3-
"version": "7.6.0",
3+
"version": "7.7.0",
44
"description": "Build and deploy e-commerce progressive web apps (PWAs) in record time.",
55
"module": "./index.js",
66
"license": "Apache-2.0",

src/props/createLazyProps.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@
4949
* component will be rendered and can display a skeleton while waiting for the promise returned
5050
* by `fetchCallback` to resolve
5151
*/
52-
export default function createLazyProps(fetchCallback, { timeout = 50 } = {}) {
52+
export default function createLazyProps(fetchCallback, { timeout = 100 } = {}) {
5353
return (options /* from getInitialProps */) => {
5454
if (typeof window === 'undefined') {
5555
// server

src/props/fetchFromAPI.js

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import fetch from '../fetch'
2-
import isBrowser from '../utils/isBrowser'
32

43
/**
54
* A convenience function to be used in `getInitialProps` to fetch data for the page from an
@@ -24,23 +23,32 @@ import isBrowser from '../utils/isBrowser'
2423
* @param {Object} opts The options object provided to `getInitialProps`
2524
* @return {Promise} A promise that resolves to the data that the page should display
2625
*/
27-
export default function fetchFromAPI({ req, asPath }) {
28-
const server = !isBrowser()
29-
const host = server ? req.headers['host'] : ''
30-
const protocol = server ? (host.startsWith('localhost') ? 'http://' : 'https://') : ''
26+
export default function fetchFromAPI({ req, asPath, pathname }) {
27+
const host = req ? process.env.API_HOST || req.headers['host'] : ''
28+
const protocol = req ? (host.startsWith('localhost') ? 'http://' : 'https://') : ''
29+
const [path, search] = asPath.split('?')
3130

32-
if (asPath === '/') asPath = ''
33-
if (asPath.startsWith('/?')) asPath = asPath.substring(1)
31+
let uri = `/api${path.replace(/\/$/, '')}`
3432

35-
let uri = `/api${asPath}`
33+
if (search) {
34+
uri += `?${search}`
35+
}
36+
37+
let headers = {}
3638

37-
if (server) {
39+
if (req) {
40+
// on the server
3841
if (uri.indexOf('?') === -1) {
3942
uri = uri + '?_includeAppData=1'
4043
} else {
4144
uri = uri + '&_includeAppData=1'
4245
}
46+
47+
headers = {
48+
host: req.headers['host'],
49+
'x-next-page': `/api${pathname.replace(/\/$/, '')}`,
50+
}
4351
}
4452

45-
return fetch(`${protocol}${host}${uri}`).then(res => res.json())
53+
return fetch(`${protocol}${host}${uri}`, { headers }).then(res => res.json())
4654
}

test/props/fetchFromAPI.test.js

Lines changed: 61 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,95 +1,124 @@
1+
import fetchFromAPI from 'react-storefront/props/fetchFromAPI'
2+
13
describe('fetchFromAPI', () => {
2-
const headers = {
3-
headers: { 'x-rsf-api-version': '1' },
4-
}
4+
let headers
55

66
fetchMock.mockResponse(JSON.stringify({}))
77

8-
let fetchFromAPI,
9-
isBrowser = true
10-
11-
beforeEach(() => {
12-
jest.isolateModules(() => {
13-
jest.doMock('react-storefront/utils/isBrowser', () => () => isBrowser)
14-
fetchFromAPI = require('react-storefront/props/fetchFromAPI').default
15-
})
16-
})
17-
18-
afterAll(() => {
19-
jest.resetAllMocks()
20-
})
21-
228
describe('in the browser', () => {
239
beforeEach(() => {
24-
isBrowser = true
10+
headers = {
11+
'x-rsf-api-version': '1',
12+
}
2513
})
2614

2715
it('should prepend api to the path', () => {
2816
fetchFromAPI({
2917
asPath: '/p/1',
3018
})
31-
expect(fetchMock).toHaveBeenCalledWith('/api/p/1', headers)
19+
expect(fetchMock).toHaveBeenCalledWith('/api/p/1', { headers })
3220
})
3321

3422
it('should call /api when the path is /', () => {
3523
fetchFromAPI({
3624
asPath: '/',
3725
})
38-
expect(fetchMock).toHaveBeenCalledWith('/api', headers)
26+
expect(fetchMock).toHaveBeenCalledWith('/api', { headers })
3927
})
4028

4129
it('should append query params directly to api if the root with query params is called', () => {
4230
fetchFromAPI({
4331
asPath: '/?test=1',
4432
})
45-
expect(fetchMock).toHaveBeenCalledWith('/api?test=1', headers)
33+
expect(fetchMock).toHaveBeenCalledWith('/api?test=1', { headers })
4634
})
4735
})
4836

4937
describe('on the server', () => {
5038
beforeEach(() => {
51-
isBrowser = false
39+
headers = {
40+
'x-rsf-api-version': '1',
41+
host: 'www.domain.com',
42+
}
5243
})
5344

5445
it('should include the protocol, domain, and ?_includeAppData=1', () => {
5546
fetchFromAPI({
5647
asPath: '/p/1',
48+
pathname: '/p/[productId]',
5749
req: {
5850
headers: {
5951
host: 'www.domain.com',
6052
},
6153
},
6254
})
63-
expect(fetchMock).toHaveBeenCalledWith(
64-
'https://www.domain.com/api/p/1?_includeAppData=1',
65-
headers,
66-
)
55+
expect(fetchMock).toHaveBeenCalledWith('https://www.domain.com/api/p/1?_includeAppData=1', {
56+
headers: {
57+
...headers,
58+
'x-next-page': '/api/p/[productId]',
59+
},
60+
})
61+
})
62+
63+
it('should use API_HOST when provided', () => {
64+
process.env.API_HOST = 'localhost:3001'
65+
66+
fetchFromAPI({
67+
asPath: '/p/1',
68+
pathname: '/p/[productId]',
69+
req: {
70+
headers: {
71+
host: 'www.domain.com',
72+
},
73+
},
74+
})
75+
76+
expect(fetchMock).toHaveBeenCalledWith('http://localhost:3001/api/p/1?_includeAppData=1', {
77+
headers: {
78+
...headers,
79+
'x-next-page': '/api/p/[productId]',
80+
},
81+
})
82+
83+
delete process.env.API_HOST
6784
})
6885

6986
it('should use http:// for localhost', () => {
7087
fetchFromAPI({
7188
asPath: '/p/1',
89+
pathname: '/p/[productId]',
7290
req: {
7391
headers: {
92+
...headers,
7493
host: 'localhost',
7594
},
7695
},
7796
})
78-
expect(fetchMock).toHaveBeenCalledWith('http://localhost/api/p/1?_includeAppData=1', headers)
97+
expect(fetchMock).toHaveBeenCalledWith('http://localhost/api/p/1?_includeAppData=1', {
98+
headers: {
99+
...headers,
100+
host: 'localhost',
101+
'x-next-page': '/api/p/[productId]',
102+
},
103+
})
79104
})
80105

81106
it('should append _includeAppData to the existing query string', () => {
82107
fetchFromAPI({
83108
asPath: '/foo?x=1',
109+
pathname: '/foo',
84110
req: {
85-
headers: {
86-
host: 'localhost',
87-
},
111+
headers,
88112
},
89113
})
90114
expect(fetchMock).toHaveBeenCalledWith(
91-
'http://localhost/api/foo?x=1&_includeAppData=1',
92-
headers,
115+
'https://www.domain.com/api/foo?x=1&_includeAppData=1',
116+
{
117+
headers: {
118+
...headers,
119+
'x-next-page': '/api/foo',
120+
},
121+
},
93122
)
94123
})
95124
})

0 commit comments

Comments
 (0)