Skip to content

Commit f1b9ae8

Browse files
update api proxy so the url can be variable (#151)
* update api proxy so the url can be variable * update strategy of getting urls * Add API URLs to GitHub Actions workflows and refactor datocms utility functions * fix env errors * add functions to netlify config * Add functions-dir to Netlify workflows * Update build process to copy Netlify functions to dist * add functions to netlify config * Refactor build and function configuration for Netlify deployment * test functions strategy * add some logging * update redirect * adjust file format * Refactor API proxy function, improve error handling and logging * Remove development logging and simplify data handling * update response * fix proxy * fix proxy * add logging * simplify * simplify even more * fix headers * fix
1 parent 82dd5bf commit f1b9ae8

11 files changed

+11399
-21053
lines changed

.env.example

+20-7
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,24 @@
1-
VUE_APP_MAPBOX_TOKEN=
1+
# Site environment variables for rws-viewer
2+
# Context: dev
3+
# Scope: any
4+
# Date: Wed, 15 Jan 2025 20:43:28 GMT
5+
6+
API_URL_OPENEARTH_DATA_VIEWER=https://kaartenbak.netlify.app
7+
API_URL_OPENEARTH_RWS_VIEWER=https://kaartenbak.netlify.app
8+
API_URL_NL2120=https://kaartenbak-2120.netlify.app
9+
10+
DATO_API_KEY_OPENEARTH_DATA_VIEWER=
11+
DATO_API_KEY_OPENEARTH_RWS_VIEWER=
12+
DATO_API_KEY_NL2120=
13+
DATO_ENVIRONMENT=main
14+
DATO_INSTANCE_CURRENT=openearth-rws-viewer
15+
16+
NODE_OPTIONS=--openssl-legacy-provider
17+
218
VUE_APP_API_ENDPOINT=
319
VUE_APP_AQUADESK_KEY=
20+
VUE_APP_MAPBOX_TOKEN=
21+
VUE_APP_PIWIK_CONTAINER_ID=
422
VUE_APP_WMR_KEY=
5-
DATO_ENVIRONMENT=
623

7-
# DatoCMS instance keys
8-
# Add keys for specific viewers here
9-
# DATO_API_KEY_[viewer]=
10-
# Corresponds to keys in config/dato/instances.mjs
11-
DATO_INSTANCE_CURRENT=openearth-rws-viewer
24+
YARN_FLAGS=--ignore-engines

.github/workflows/main-migrations.yaml

+5
Original file line numberDiff line numberDiff line change
@@ -117,17 +117,22 @@ jobs:
117117
DATO_API_KEY_NL2120: ${{ secrets.DATO_API_KEY_NL2120 }}
118118
DATO_INSTANCE_CURRENT: ${{ matrix.name }}
119119
DATO_ENVIRONMENT: main
120+
API_URL_OPENEARTH_DATA_VIEWER: ${{ secrets.API_URL_OPENEARTH_DATA_VIEWER }}
121+
API_URL_OPENEARTH_RWS_VIEWER: ${{ secrets.API_URL_OPENEARTH_RWS_VIEWER }}
122+
API_URL_NL2120: ${{ secrets.API_URL_NL2120 }}
120123
VUE_APP_MAPBOX_TOKEN: ${{ secrets.VUE_APP_MAPBOX_TOKEN }}
121124
VUE_APP_API_ENDPOINT: ${{ secrets.VUE_APP_API_ENDPOINT }}
122125
VUE_APP_AQUADESK_KEY: ${{ secrets.VUE_APP_AQUADESK_KEY }}
123126
VUE_APP_PIWIK_CONTAINER_ID: ${{ secrets.VUE_APP_PIWIK_CONTAINER_ID }}
124127
VUE_APP_WMR_KEY: ${{ secrets.VUE_APP_WMR_KEY }}
125128
DEEPL_KEY: ${{ secrets.DEEPL_KEY }}
129+
YARN_FLAGS: --ignore-engines
126130

127131
- name: Deploy to Netlify
128132
uses: nwtgck/[email protected]
129133
with:
130134
publish-dir: "./dist"
135+
functions-dir: "./out/netlify/functions"
131136
production-branch: main
132137
github-token: ${{ secrets.GITHUB_TOKEN }}
133138
env:

.github/workflows/staging-migrations.yaml

+5
Original file line numberDiff line numberDiff line change
@@ -94,17 +94,22 @@ jobs:
9494
DATO_API_KEY_NL2120: ${{ secrets.DATO_API_KEY_NL2120 }}
9595
DATO_INSTANCE_CURRENT: ${{ matrix.name }}
9696
DATO_ENVIRONMENT: staging
97+
API_URL_OPENEARTH_DATA_VIEWER: ${{ secrets.API_URL_OPENEARTH_DATA_VIEWER }}
98+
API_URL_OPENEARTH_RWS_VIEWER: ${{ secrets.API_URL_OPENEARTH_RWS_VIEWER }}
99+
API_URL_NL2120: ${{ secrets.API_URL_NL2120 }}
97100
VUE_APP_MAPBOX_TOKEN: ${{ secrets.VUE_APP_MAPBOX_TOKEN }}
98101
VUE_APP_API_ENDPOINT: ${{ secrets.VUE_APP_API_ENDPOINT }}
99102
VUE_APP_AQUADESK_KEY: ${{ secrets.VUE_APP_AQUADESK_KEY }}
100103
VUE_APP_PIWIK_CONTAINER_ID: ${{ secrets.VUE_APP_PIWIK_CONTAINER_ID }}
101104
VUE_APP_WMR_KEY: ${{ secrets.VUE_APP_WMR_KEY }}
102105
DEEPL_KEY: ${{ secrets.DEEPL_KEY }}
106+
YARN_FLAGS: --ignore-engines
103107

104108
- name: Deploy to Netlify
105109
uses: nwtgck/[email protected]
106110
with:
107111
publish-dir: "./dist"
112+
functions-dir: "./out/netlify/functions"
108113
production-branch: main
109114
github-token: ${{ secrets.GITHUB_TOKEN }}
110115
enable-commit-comment: false

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
.DS_Store
22
node_modules
33
/dist
4+
/out
45

56
# local env files
67
.env

config/dato/datocms.mjs

+4-4
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ import slugify from '@sindresorhus/slugify'
44
import { datoEnvironment } from './constants.mjs'
55
import { instances } from './instances.js'
66

7-
const capitaliseFirstLetter = ([ first, ...rest ]) => first.toUpperCase() + rest.join('')
8-
const lowerCaseFirstLetter = ([ first, ...rest ]) => first.toLowerCase() + rest.join('')
7+
const capitaliseFirstLetter = ([first, ...rest]) => first.toUpperCase() + rest.join('')
8+
const lowerCaseFirstLetter = ([first, ...rest]) => first.toLowerCase() + rest.join('')
99
const camelCase = pipe(
1010
slugify,
1111
split('-'),
@@ -37,7 +37,7 @@ function executeFetch(variables, query) {
3737
headers: {
3838
'Content-Type': 'application/json',
3939
Accept: 'application/json',
40-
Authorization: `Bearer ${ token }`,
40+
Authorization: `Bearer ${token}`,
4141
'X-Environment': datoEnvironment,
4242
},
4343
data: { query, variables },
@@ -62,7 +62,7 @@ function getPaginatedData(variables, query) {
6262

6363
if (allKey) {
6464
const { count } = response.data.data[allKey]
65-
const [ , originalKey ] = allKey.match(keyRegex).map(camelCase)
65+
const [, originalKey] = allKey.match(keyRegex).map(camelCase)
6666
const itemsInResponse = response.data.data[originalKey]
6767
const remainingAmount = count - itemsInResponse.length
6868

config/dato/instances.d.ts

+1
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@ export const instances: Array<{
22
name: string;
33
datoApiKey: string;
44
netlifySiteId: string;
5+
apiUrl: string;
56
}>;

config/dato/instances.js

+9-2
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,28 @@
1-
const dotenv = require("dotenv");
1+
// If the API keys are not set, load them from the .env file
2+
if (!process.env.DATO_API_KEY_NL2120) {
3+
const dotenv = require("dotenv");
4+
5+
dotenv.config();
6+
}
27

3-
dotenv.config();
48

59
module.exports.instances = [
610
{
711
name: "nl2120",
812
datoApiKey: process.env.DATO_API_KEY_NL2120,
913
netlifySiteId: "1f6372f6-c532-4e0e-bba7-dee08678d518",
14+
apiUrl: process.env.API_URL_NL2120,
1015
},
1116
{
1217
name: "openearth-data-viewer",
1318
datoApiKey: process.env.DATO_API_KEY_OPENEARTH_DATA_VIEWER,
1419
netlifySiteId: "1785f3f6-b4cf-42de-bea6-b57d48a5c664",
20+
apiUrl: process.env.API_URL_OPENEARTH_DATA_VIEWER,
1521
},
1622
{
1723
name: "openearth-rws-viewer",
1824
datoApiKey: process.env.DATO_API_KEY_OPENEARTH_RWS_VIEWER,
1925
netlifySiteId: "119b8ff3-5b22-4995-b43b-b31f21ba77c3",
26+
apiUrl: process.env.API_URL_OPENEARTH_RWS_VIEWER,
2027
},
2128
];

netlify.toml

+12-12
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,21 @@
11
[build]
2-
command = "npm run build"
3-
publish = "dist/"
2+
command = "vue-cli-service build"
3+
publish = "dist/"
44

55
[[redirects]]
6-
from = "https://rws-viewer.netlify.app/*"
7-
to = "https://viewer.openearth.nl/:splat"
8-
status = 301
6+
from = "https://rws-viewer.netlify.app/*"
7+
to = "https://viewer.openearth.nl/:splat"
8+
status = 301
99

1010
[[redirects]]
11-
from = "/api/*"
12-
to = "https://kaartenbak.netlify.app/api/:splat"
13-
status = 200
11+
from = "/api/*"
12+
to = "/.netlify/functions/api"
13+
status = 200
1414

1515
[[redirects]]
16-
from = "/*"
17-
to = "/index.html"
18-
status = 200
16+
from = "/*"
17+
to = "/index.html"
18+
status = 200
1919

2020
[dev]
21-
targetPort = 3000
21+
targetPort = 3000

netlify/functions/api.mjs

+117
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
const { instances } = require("../../config/dato/instances");
2+
3+
// Change to exports.handler format
4+
exports.handler = async (event, context) => {
5+
try {
6+
// Find the current instance
7+
const currentInstance = instances.find(
8+
(instance) => instance.name === process.env.DATO_INSTANCE_CURRENT
9+
);
10+
11+
if (!currentInstance) {
12+
return createErrorResponse("Current instance not found", 500);
13+
}
14+
15+
const apiUrl = `${currentInstance.apiUrl}/api`;
16+
17+
if (!apiUrl) {
18+
return createErrorResponse("API_URL is not configured", 500);
19+
}
20+
21+
// Get the path and query parameters from the request URL
22+
const eventUrl = new URL(event.rawUrl || `https://${event.headers.host}${event.path}`);
23+
const path = eventUrl.pathname.replace('/.netlify/functions/api', '');
24+
25+
// Create the target URL with the path
26+
const targetUrl = new URL(path, apiUrl);
27+
28+
// Copy all query parameters from the original request URL
29+
for (const [key, value] of eventUrl.searchParams.entries()) {
30+
targetUrl.searchParams.set(key, value);
31+
}
32+
33+
// Also check event.queryStringParameters as a fallback for compatibility
34+
const params = event.queryStringParameters || {};
35+
Object.keys(params).forEach(key => {
36+
// Only set if not already set from URL search params
37+
if (!targetUrl.searchParams.has(key)) {
38+
targetUrl.searchParams.set(key, params[key]);
39+
}
40+
});
41+
42+
// Convert to string for the fetch request
43+
const targetUrlString = targetUrl.toString();
44+
45+
// Prepare headers to forward
46+
const headers = {};
47+
48+
// Forward relevant headers from the original request
49+
if (event.headers) {
50+
const headersToForward = [
51+
'authorization',
52+
'content-type',
53+
'accept',
54+
'user-agent',
55+
'x-requested-with'
56+
];
57+
58+
for (const header of headersToForward) {
59+
if (event.headers[header]) {
60+
headers[header] = event.headers[header];
61+
}
62+
}
63+
}
64+
65+
// Prepare fetch options
66+
const fetchOptions = {
67+
method: event.httpMethod || 'GET',
68+
headers
69+
};
70+
71+
// Forward request body for POST, PUT, PATCH methods
72+
if (['POST', 'PUT', 'PATCH'].includes(fetchOptions.method) && event.body) {
73+
fetchOptions.body = event.body;
74+
}
75+
76+
// Log request details (only in development)
77+
if (process.env.NODE_ENV !== 'production') {
78+
console.log(`Proxying ${fetchOptions.method} request to: ${targetUrlString}`);
79+
}
80+
81+
console.log('[TARGET URL]', targetUrlString);
82+
83+
// Fetch the target URL and properly handle its response
84+
const response = await fetch(targetUrlString, fetchOptions);
85+
86+
const json = await response.json();
87+
88+
console.log('[RESPONSE]', json);
89+
console.log('[RESPONSE STATUS]', response.status);
90+
91+
// Return in the format expected by Netlify Functions
92+
return {
93+
statusCode: response.status,
94+
body: JSON.stringify(json),
95+
headers: {
96+
'Content-Type': 'application/json',
97+
}
98+
};
99+
} catch (error) {
100+
console.error("Error proxying request:", error);
101+
return createErrorResponse("Error proxying request to API", 500);
102+
}
103+
};
104+
105+
// Helper function to create error responses (updated for Netlify Functions format)
106+
function createErrorResponse(message, statusCode) {
107+
return {
108+
statusCode,
109+
body: JSON.stringify({ error: message }),
110+
headers: {
111+
"Content-Type": "application/json",
112+
"Access-Control-Allow-Origin": "*"
113+
}
114+
};
115+
}
116+
117+
// The path configuration is handled differently in netlify.toml for traditional functions

0 commit comments

Comments
 (0)