Skip to content

Commit 974efc6

Browse files
authored
fix: improve handling of API errors (#454)
1 parent 91d6079 commit 974efc6

File tree

8 files changed

+104
-92
lines changed

8 files changed

+104
-92
lines changed

lib/routes.ts

Lines changed: 37 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -154,22 +154,17 @@ export function setupRoutes(app: Express, ddbApi: DynamoDbApi): void {
154154
}
155155
}
156156

157-
try {
158-
await ddbApi.createTable({
159-
TableName,
160-
ProvisionedThroughput: {
161-
ReadCapacityUnits,
162-
WriteCapacityUnits,
163-
},
164-
GlobalSecondaryIndexes: globalSecondaryIndexes.length ? globalSecondaryIndexes : undefined,
165-
LocalSecondaryIndexes: localSecondaryIndexes.length ? localSecondaryIndexes : undefined,
166-
KeySchema: keySchema,
167-
AttributeDefinitions: attributeDefinitions,
168-
});
169-
} catch (error) {
170-
res.status(400).send(error);
171-
return;
172-
}
157+
await ddbApi.createTable({
158+
TableName,
159+
ProvisionedThroughput: {
160+
ReadCapacityUnits,
161+
WriteCapacityUnits,
162+
},
163+
GlobalSecondaryIndexes: globalSecondaryIndexes.length ? globalSecondaryIndexes : undefined,
164+
LocalSecondaryIndexes: localSecondaryIndexes.length ? localSecondaryIndexes : undefined,
165+
KeySchema: keySchema,
166+
AttributeDefinitions: attributeDefinitions,
167+
});
173168

174169
res.status(204).end();
175170
}),
@@ -327,39 +322,34 @@ export function setupRoutes(app: Express, ddbApi: DynamoDbApi): void {
327322
};
328323
const pageSize = typeof req.query.pageSize === 'string' ? Number.parseInt(req.query.pageSize) : 25;
329324

330-
try {
331-
const results = await getPage(ddbApi, tableDescription.KeySchema!, TableName, params, pageSize, operationType);
332-
const { pageItems, nextKey } = results;
325+
const results = await getPage(ddbApi, tableDescription.KeySchema!, TableName, params, pageSize, operationType);
326+
const { pageItems, nextKey } = results;
333327

334-
const primaryKeys = tableDescription.KeySchema!.map(schema => schema.AttributeName);
335-
// Primary keys are listed first.
336-
const uniqueKeys = [
337-
...primaryKeys,
338-
...extractKeysForItems(pageItems).filter(key => !primaryKeys.includes(key)),
339-
];
328+
const primaryKeys = tableDescription.KeySchema!.map(schema => schema.AttributeName);
329+
// Primary keys are listed first.
330+
const uniqueKeys = [
331+
...primaryKeys,
332+
...extractKeysForItems(pageItems).filter(key => !primaryKeys.includes(key)),
333+
];
340334

341-
// Append the item key.
342-
for (const item of pageItems) {
343-
item.__key = extractKey(item, tableDescription.KeySchema!);
344-
}
335+
// Append the item key.
336+
for (const item of pageItems) {
337+
item.__key = extractKey(item, tableDescription.KeySchema!);
338+
}
345339

346-
const data = {
347-
query: req.query,
348-
pageNum,
349-
prevKey: encodeURIComponent(typeof req.query.prevKey === 'string' ? req.query.prevKey : ''),
350-
startKey: encodeURIComponent(typeof req.query.startKey === 'string' ? req.query.startKey : ''),
351-
nextKey: nextKey ? encodeURIComponent(JSON.stringify(nextKey)) : null,
352-
filterQueryString: encodeURIComponent(typeof req.query.filters === 'string' ? req.query.filters : ''),
353-
Table: tableDescription,
354-
Items: pageItems,
355-
uniqueKeys,
356-
};
340+
const data = {
341+
query: req.query,
342+
pageNum,
343+
prevKey: encodeURIComponent(typeof req.query.prevKey === 'string' ? req.query.prevKey : ''),
344+
startKey: encodeURIComponent(typeof req.query.startKey === 'string' ? req.query.startKey : ''),
345+
nextKey: nextKey ? encodeURIComponent(JSON.stringify(nextKey)) : null,
346+
filterQueryString: encodeURIComponent(typeof req.query.filters === 'string' ? req.query.filters : ''),
347+
Table: tableDescription,
348+
Items: pageItems,
349+
uniqueKeys,
350+
};
357351

358-
res.json(data);
359-
} catch (error: any) {
360-
const typedError: Error & { code?: number } = error;
361-
res.status(400).send((typedError.code ? '[' + typedError.code + '] ' : '') + typedError.message);
362-
}
352+
res.json(data);
363353
}));
364354

365355
app.get('/tables/:TableName/meta', asyncMiddleware(async(req, res) => {
@@ -453,8 +443,7 @@ export function setupRoutes(app: Express, ddbApi: DynamoDbApi): void {
453443
res.json(response.Item);
454444
}));
455445

456-
app.use(((err, _req, _res, next) => {
457-
console.error(err);
458-
next(err);
446+
app.use(((error, _req, res, _next) => {
447+
res.status(500).send(error);
459448
}) as ErrorRequestHandler);
460449
}

views/create-table.ejs

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,8 @@
6363
if (response.ok) {
6464
window.location = '/'
6565
} else {
66-
const value = await response.text()
67-
$('#error-wrapper').html('<div class="alert alert-danger" role="alert">' + JSON.parse(value).message + '</div>')
66+
const error = await response.text()
67+
handleError(JSON.parse(error).message)
6868
}
6969
})
7070
@@ -182,16 +182,12 @@
182182
]
183183
%>
184184
<%- include('partials/breadcrumb', { breadcrumb }) %>
185+
<%- include('partials/error-container') %>
185186
</header>
186187
<main>
187188
<form id="form" method="post">
188189
<div class="container">
189190
<h1>Create Table</h1>
190-
<div class="row">
191-
<div class="col">
192-
<span id="error-wrapper"></span>
193-
</div>
194-
</div>
195191
<div class="row">
196192
<div class="col-md-12">
197193
<div class="form-group">

views/item.ejs

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
}
2929
%>
3030
<%- include('partials/breadcrumb', { breadcrumb }) %>
31+
<%- include('partials/error-container') %>
3132
</header>
3233

3334
<main class='flex-fill d-flex flex-column'>
@@ -42,11 +43,12 @@
4243
'Content-Type': 'application/json'
4344
},
4445
body: editor.getValue()
45-
}).then((response) => {
46+
}).then(async (response) => {
4647
if (response.ok) {
4748
return response.json()
4849
} else {
49-
throw new Error()
50+
const responseText = await response.text()
51+
throw new Error(JSON.parse(responseText).message)
5052
}
5153
}).then((json) => {
5254
if (document.location.pathname.endsWith('/add-item')) {
@@ -72,7 +74,7 @@
7274
saveButton.classList.add('btn-primary')
7375
saveButton.classList.remove('btn-warning')
7476
}, 2000)
75-
console.error(error)
77+
handleError(error.message)
7678
})
7779
}
7880
</script>
@@ -90,13 +92,15 @@
9092
event.preventDefault()
9193
fetch(document.location.pathname, {
9294
method: 'delete'
93-
}).then((response) => {
95+
}).then(async (response) => {
9496
if (response.ok) {
9597
window.location = `/tables/<%= encodeURIComponent(TableName) %>`
98+
} else {
99+
const responseText = await response.text()
100+
throw new Error(JSON.parse(responseText).message)
96101
}
97102
}).catch((error) => {
98-
console.error(error)
99-
alert('There was an error.')
103+
handleError(error.message)
100104
})
101105
}
102106
</script>

views/meta.ejs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@
3838
<a class='nav-link active' href='/tables/<%= encodeURIComponent(Table.TableName) %>/meta'>Meta</a>
3939
</li>
4040
</ul>
41+
42+
<%- include('partials/error-container') %>
4143
</header>
4244

4345
<main class="flex-fill d-flex flex-column">
@@ -55,13 +57,14 @@
5557
document.querySelector('#deleteTable').addEventListener('click', () => {
5658
fetch('/tables/<%= encodeURIComponent(Table.TableName) %>', {
5759
method: 'delete'
58-
}).then((response) => {
60+
}).then(async (response) => {
5961
if (!response.ok) {
60-
throw new Error
62+
const responseText = await response.text()
63+
throw new Error(JSON.parse(responseText).message)
6164
}
6265
window.location.href = '/'
63-
}).catch(() => {
64-
window.alert('There was an error when attempting to delete the table.')
66+
}).catch((error) => {
67+
handleError(error.message)
6568
})
6669
});
6770

views/partials/bootstrap-deps.ejs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,16 @@
77
<script>
88
$(function () {
99
$('[data-toggle="tooltip"]').tooltip()
10-
})
10+
});
11+
12+
function handleError(errorText) {
13+
const errorEl = $('#error-wrapper')
14+
if (errorEl.length) {
15+
$('#error-wrapper').html('<div class="alert alert-danger" role="alert">' + errorText + '</div>')[0].scrollIntoView({
16+
behavior: 'smooth',
17+
});
18+
} else {
19+
window.alert(errorText)
20+
}
21+
}
1122
</script>

views/partials/error-container.ejs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<div class="row">
2+
<div class="col">
3+
<div id="error-wrapper"></div>
4+
</div>
5+
</div>

views/scan.ejs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,8 @@
105105
<a href="/tables/<%= encodeURIComponent(Table.TableName) %>/add-item" class="nav-link">Create item</a>
106106
</li>
107107
</ul>
108+
109+
<%- include('partials/error-container') %>
108110
</header>
109111

110112
<main>
@@ -501,18 +503,19 @@
501503
}
502504
503505
fetch(document.location.pathname + '/items' + document.location.search)
504-
.then(response => {
506+
.then(async (response) => {
505507
if (response.ok) {
506508
return response.json()
507509
}
508510
509-
return response.text().then(text => Promise.reject(new Error(text)))
511+
const responseText = await response.text()
512+
throw new Error(JSON.parse(responseText).message)
510513
})
511514
.then(json => {
512515
renderItems(json)
513516
})
514517
.catch(error => {
515-
alert('Error loading data: ' + error)
518+
handleError(error.message)
516519
})
517520
})
518521
</script>

views/tables.ejs

Lines changed: 24 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
]
2020
%>
2121
<%- include('partials/breadcrumb', { breadcrumb }) %>
22+
<%- include('partials/error-container') %>
2223
</header>
2324
<main>
2425
<script>
@@ -60,14 +61,15 @@
6061
fetch(`/tables/${TableName}`, {
6162
method: 'delete'
6263
})
63-
.then((response) => {
64+
.then(async (response) => {
6465
if (!response.ok) {
65-
throw new Error
66+
const responseText = await response.text()
67+
throw new Error(JSON.parse(responseText).message)
6668
}
6769
location.reload()
6870
})
6971
.catch((err) => {
70-
window.alert(`There was an error when attempting to delete the table:\n\n${err}`)
72+
handleError(error.message)
7173
})
7274
}
7375
@@ -78,13 +80,14 @@
7880
7981
fetch(`/tables/${TableName}/all`, {
8082
method: 'delete'
81-
}).then((response) => {
83+
}).then(async (response) => {
8284
if (!response.ok) {
83-
throw new Error
85+
const responseText = await response.text()
86+
throw new Error(JSON.parse(responseText).message)
8487
}
8588
location.reload()
86-
}).catch(() => {
87-
window.alert('There was an error when attempting to purge the table.')
89+
}).catch((error) => {
90+
handleError(error.message)
8891
})
8992
}
9093
@@ -94,16 +97,15 @@
9497
}
9598
fetch('/tables-purge', {
9699
method: 'delete'
97-
}).then((response) => {
100+
}).then(async (response) => {
101+
const responseText = await response.text()
98102
if (!response.ok) {
99-
throw new Error
103+
throw new Error(JSON.parse(responseText).message)
100104
}
101-
response.text().then(text => {
102-
window.alert(text)
103-
location.reload()
104-
})
105-
}).catch(() => {
106-
window.alert('There was an error when attempting to purge all tables.')
105+
window.alert(responseText)
106+
location.reload()
107+
}).catch((error) => {
108+
handleError(error.message)
107109
})
108110
}
109111
@@ -113,16 +115,15 @@
113115
}
114116
fetch('/tables', {
115117
method: 'delete'
116-
}).then((response) => {
118+
}).then(async (response) => {
119+
const responseText = await response.text()
117120
if (!response.ok) {
118-
throw new Error
121+
throw new Error(JSON.parse(responseText).message)
119122
}
120-
response.text().then(text => {
121-
window.alert(text)
122-
location.reload()
123-
})
124-
}).catch(() => {
125-
window.alert('There was an error when attempting to delete all tables.')
123+
window.alert(responseText)
124+
location.reload()
125+
}).catch((error) => {
126+
handleError(error.message)
126127
})
127128
}
128129

0 commit comments

Comments
 (0)