Initial commit

This commit is contained in:
julien Lengrand-Lambert
2021-08-11 15:49:22 +02:00
commit e004d0c959
17 changed files with 731 additions and 0 deletions

5
.vscode/extensions.json vendored Normal file
View File

@@ -0,0 +1,5 @@
{
"recommendations": [
"ms-azuretools.vscode-azurefunctions"
]
}

12
.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,12 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "Attach to Node Functions",
"type": "node",
"request": "attach",
"port": 9229,
"preLaunchTask": "func: host start"
}
]
}

8
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,8 @@
{
"azureFunctions.deploySubpath": "api",
"azureFunctions.postDeployTask": "npm install",
"azureFunctions.projectLanguage": "JavaScript",
"azureFunctions.projectRuntime": "~3",
"debug.internalConsoleOptions": "neverOpen",
"azureFunctions.preDeployTask": "npm prune"
}

32
.vscode/tasks.json vendored Normal file
View File

@@ -0,0 +1,32 @@
{
"version": "2.0.0",
"tasks": [
{
"type": "func",
"command": "host start",
"problemMatcher": "$func-node-watch",
"isBackground": true,
"dependsOn": "npm install",
"options": {
"cwd": "${workspaceFolder}/api"
}
},
{
"type": "shell",
"label": "npm install",
"command": "npm install",
"options": {
"cwd": "${workspaceFolder}/api"
}
},
{
"type": "shell",
"label": "npm prune",
"command": "npm prune --production",
"problemMatcher": [],
"options": {
"cwd": "${workspaceFolder}/api"
}
}
]
}

11
README.md Normal file
View File

@@ -0,0 +1,11 @@
# Static Web Apps - Mongoose starter
This project is designed to be a starter [Azure Static Web Apps](https://docs.microsoft.com/azure/static-web-apps/overview) with [Mongoose](https://mongoosejs.com/). It uses the following resources:
- Azure resources
- Azure Static Web Apps
- Cosmos DB with Mongo API
- Application libraries
- Vanilla JavaScript
- Mongoose
- Azure Functions

7
api/.funcignore Normal file
View File

@@ -0,0 +1,7 @@
*.js.map
*.ts
.git*
.vscode
local.settings.json
test
tsconfig.json

94
api/.gitignore vendored Normal file
View File

@@ -0,0 +1,94 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# TypeScript v1 declaration files
typings/
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
.env.test
# parcel-bundler cache (https://parceljs.org/)
.cache
# next.js build output
.next
# nuxt.js build output
.nuxt
# vuepress build output
.vuepress/dist
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TypeScript output
dist
out
# Azure Functions artifacts
bin
obj
appsettings.json
local.settings.json

15
api/host.json Normal file
View File

@@ -0,0 +1,15 @@
{
"version": "2.0",
"logging": {
"applicationInsights": {
"samplingSettings": {
"isEnabled": true,
"excludedTypes": "Request"
}
}
},
"extensionBundle": {
"id": "Microsoft.Azure.Functions.ExtensionBundle",
"version": "[1.*, 2.0.0)"
}
}

241
api/package-lock.json generated Normal file
View File

@@ -0,0 +1,241 @@
{
"name": "api",
"version": "1.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"bl": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/bl/-/bl-2.2.1.tgz",
"integrity": "sha512-6Pesp1w0DEX1N550i/uGV/TqucVL4AM/pgThFSN/Qq9si1/DF9aIHs1BxD8V/QU0HoeHO6cQRTAuYnLPKq1e4g==",
"requires": {
"readable-stream": "^2.3.5",
"safe-buffer": "^5.1.1"
}
},
"bluebird": {
"version": "3.5.1",
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz",
"integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA=="
},
"bson": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/bson/-/bson-1.1.5.tgz",
"integrity": "sha512-kDuEzldR21lHciPQAIulLs1LZlCXdLziXI6Mb/TDkwXhb//UORJNPXgcRs2CuO4H0DcMkpfT3/ySsP3unoZjBg=="
},
"core-util-is": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
},
"debug": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
"requires": {
"ms": "2.0.0"
},
"dependencies": {
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
}
}
},
"denque": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/denque/-/denque-1.4.1.tgz",
"integrity": "sha512-OfzPuSZKGcgr96rf1oODnfjqBFmr1DVoc/TrItj3Ohe0Ah1C5WX5Baquw/9U9KovnQ88EqmJbD66rKYUQYN1tQ=="
},
"inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
"isarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
},
"kareem": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/kareem/-/kareem-2.3.1.tgz",
"integrity": "sha512-l3hLhffs9zqoDe8zjmb/mAN4B8VT3L56EUvKNqLFVs9YlFA+zx7ke1DO8STAdDyYNkeSo1nKmjuvQeI12So8Xw=="
},
"memory-pager": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz",
"integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==",
"optional": true
},
"mongodb": {
"version": "3.6.2",
"resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.6.2.tgz",
"integrity": "sha512-sSZOb04w3HcnrrXC82NEh/YGCmBuRgR+C1hZgmmv4L6dBz4BkRse6Y8/q/neXer9i95fKUBbFi4KgeceXmbsOA==",
"requires": {
"bl": "^2.2.1",
"bson": "^1.1.4",
"denque": "^1.4.1",
"require_optional": "^1.0.1",
"safe-buffer": "^5.1.2",
"saslprep": "^1.0.0"
}
},
"mongoose": {
"version": "5.10.11",
"resolved": "https://registry.npmjs.org/mongoose/-/mongoose-5.10.11.tgz",
"integrity": "sha512-R5BFitKW94/S/Z48w+X+qi/eto66jWBcVEVA8nYVkBoBAPFGq7JSYP/0uso+ZHs+7XjSzTuui+SUllzxIrf9yA==",
"requires": {
"bson": "^1.1.4",
"kareem": "2.3.1",
"mongodb": "3.6.2",
"mongoose-legacy-pluralize": "1.0.2",
"mpath": "0.7.0",
"mquery": "3.2.2",
"ms": "2.1.2",
"regexp-clone": "1.0.0",
"safe-buffer": "5.2.1",
"sift": "7.0.1",
"sliced": "1.0.1"
}
},
"mongoose-legacy-pluralize": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/mongoose-legacy-pluralize/-/mongoose-legacy-pluralize-1.0.2.tgz",
"integrity": "sha512-Yo/7qQU4/EyIS8YDFSeenIvXxZN+ld7YdV9LqFVQJzTLye8unujAWPZ4NWKfFA+RNjh+wvTWKY9Z3E5XM6ZZiQ=="
},
"mpath": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/mpath/-/mpath-0.7.0.tgz",
"integrity": "sha512-Aiq04hILxhz1L+f7sjGyn7IxYzWm1zLNNXcfhDtx04kZ2Gk7uvFdgZ8ts1cWa/6d0TQmag2yR8zSGZUmp0tFNg=="
},
"mquery": {
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/mquery/-/mquery-3.2.2.tgz",
"integrity": "sha512-XB52992COp0KP230I3qloVUbkLUxJIu328HBP2t2EsxSFtf4W1HPSOBWOXf1bqxK4Xbb66lfMJ+Bpfd9/yZE1Q==",
"requires": {
"bluebird": "3.5.1",
"debug": "3.1.0",
"regexp-clone": "^1.0.0",
"safe-buffer": "5.1.2",
"sliced": "1.0.1"
},
"dependencies": {
"safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
}
}
},
"ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
},
"process-nextick-args": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
},
"readable-stream": {
"version": "2.3.7",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
"integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
"requires": {
"core-util-is": "~1.0.0",
"inherits": "~2.0.3",
"isarray": "~1.0.0",
"process-nextick-args": "~2.0.0",
"safe-buffer": "~5.1.1",
"string_decoder": "~1.1.1",
"util-deprecate": "~1.0.1"
},
"dependencies": {
"safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
}
}
},
"regexp-clone": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/regexp-clone/-/regexp-clone-1.0.0.tgz",
"integrity": "sha512-TuAasHQNamyyJ2hb97IuBEif4qBHGjPHBS64sZwytpLEqtBQ1gPJTnOaQ6qmpET16cK14kkjbazl6+p0RRv0yw=="
},
"require_optional": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/require_optional/-/require_optional-1.0.1.tgz",
"integrity": "sha512-qhM/y57enGWHAe3v/NcwML6a3/vfESLe/sGM2dII+gEO0BpKRUkWZow/tyloNqJyN6kXSl3RyyM8Ll5D/sJP8g==",
"requires": {
"resolve-from": "^2.0.0",
"semver": "^5.1.0"
}
},
"resolve-from": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz",
"integrity": "sha1-lICrIOlP+h2egKgEx+oUdhGWa1c="
},
"safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
},
"saslprep": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/saslprep/-/saslprep-1.0.3.tgz",
"integrity": "sha512-/MY/PEMbk2SuY5sScONwhUDsV2p77Znkb/q3nSVstq/yQzYJOH/Azh29p9oJLsl3LnQwSvZDKagDGBsBwSooag==",
"optional": true,
"requires": {
"sparse-bitfield": "^3.0.3"
}
},
"semver": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
},
"sift": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/sift/-/sift-7.0.1.tgz",
"integrity": "sha512-oqD7PMJ+uO6jV9EQCl0LrRw1OwsiPsiFQR5AR30heR+4Dl7jBBbDLnNvWiak20tzZlSE1H7RB30SX/1j/YYT7g=="
},
"sliced": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/sliced/-/sliced-1.0.1.tgz",
"integrity": "sha1-CzpmK10Ewxd7GSa+qCsD+Dei70E="
},
"sparse-bitfield": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz",
"integrity": "sha1-/0rm5oZWBWuks+eSqzM004JzyhE=",
"optional": true,
"requires": {
"memory-pager": "^1.0.2"
}
},
"string_decoder": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"requires": {
"safe-buffer": "~5.1.0"
},
"dependencies": {
"safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
}
}
},
"util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
}
}
}

13
api/package.json Normal file
View File

@@ -0,0 +1,13 @@
{
"name": "api",
"version": "1.0.0",
"description": "",
"scripts": {
"start": "func start",
"test": "echo \"No tests yet...\""
},
"dependencies": {
"mongoose": "^5.10.11"
},
"devDependencies": {}
}

4
api/proxies.json Normal file
View File

@@ -0,0 +1,4 @@
{
"$schema": "http://json.schemastore.org/proxies",
"proxies": {}
}

21
api/tasks/function.json Normal file
View File

@@ -0,0 +1,21 @@
{
"bindings": [
{
"authLevel": "anonymous",
"type": "httpTrigger",
"direction": "in",
"name": "req",
"methods": [
"get",
"post",
"put"
],
"route": "tasks/{id?}"
},
{
"type": "http",
"direction": "out",
"name": "res"
}
]
}

88
api/tasks/index.js Normal file
View File

@@ -0,0 +1,88 @@
// Load mongoose
const mongoose = require('mongoose');
// Connect to the database
mongoose.connect(
process.env.CONNECTION_STRING, // Retrieve connection string
{ // boiler plate values
useNewUrlParser: true,
useUnifiedTopology: true,
}
);
// Create the schema or structure of our object in Mongoose
const taskSchema = new mongoose.Schema({
title: String, // Add title property of type string
completed: { // Add completed property
type: Boolean, // Set type to boolean
default: false // Set default to false
}
});
// Create a model using our schema
// This model will be used to access the database
const TaskModel = mongoose.model('task', taskSchema);
// Export our function
module.exports = async function (context, req) {
// setup our default content type (we always return JSON)
context.res = {
header: {
"Content-Type": "application/json"
}
}
// Read the method and determine the requested action
switch (req.method) {
// If get, return all tasks
case 'GET':
await getTasks(context);
break;
// If post, create new task
case 'POST':
await createTask(context);
break;
// If put, update task
case 'PUT':
await updateTask(context);
break;
}
};
// Return all tasks
async function getTasks(context) {
// load all tasks from database
const tasks = await TaskModel.find();
// return all tasks
context.res.body = { tasks: tasks };
}
// Create new task
async function createTask(context) {
// Read the uploaded task
const body = context.req.body;
// Save to database
const task = await TaskModel.create(body);
// Set the HTTP status to created
context.res.status = 201;
// return new object
context.res.body = task;
}
// Update an existing function
async function updateTask(context) {
// Grab the id from the URL (stored in bindingData)
const id = context.bindingData.id;
// Get the task from the body
const task = context.req.body;
// Update the item in the database
const result = await TaskModel.updateOne({ _id: id }, task);
// Check to ensure an item was modified
if (result.nModified === 1) {
// Updated an item, status 204 (empty update)
context.res.status = 204;
} else {
// Item not found, status 404
context.res.status = 404;
}
}

3
api/tasks/sample.dat Normal file
View File

@@ -0,0 +1,3 @@
{
"name": "Azure"
}

12
public/index.css Normal file
View File

@@ -0,0 +1,12 @@
body {
font-family: Verdana, Geneva, Tahoma, sans-serif;
font-size: 16px;
}
.completed {
text-decoration: line-through;
}
ul {
list-style-type: none;
}

30
public/index.html Normal file
View File

@@ -0,0 +1,30 @@
<!doctype html>
<html lang="en">
<head>
<title>Tasks</title>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<!-- Bootstrap CSS -->
<link rel="stylesheet" href="index.css">
</head>
<body>
<article>
<h1>My tasks</h1>
<!-- List for tasks -->
<ul id="task-list"></ul>
<div>
<label for="name">Title: </label><input type="text" id="task-title" />
</div>
<div>
<button type="button" id="task-register">Add task</button>
</div>
</article>
<script src="./local-index.js"></script>
</body>
</html>

135
public/local-index.js Normal file
View File

@@ -0,0 +1,135 @@
// run the code
main();
// stores all tasks
const tasks = [];
// entry function
async function main() {
await loadTasks();
displayTasks();
}
// Calls server to retrieve all tasks
async function loadTasks() {
try {
// Uses fetch to call server
const response = await fetch('/api/tasks');
// Reads returned JSON, which contains one property called tasks
const retrievedData = await response.json();
// Retrieve tasks, which contains an array of all tasks in database
const retrievedTasks = retrievedData.tasks;
// Loop through all tasks
for (let task of retrievedTasks) {
// Add each task to the array
tasks.push(task);
}
} catch {
// If there is an error, display a generic message on the page
const messageElement = document.createElement('li');
messageElement.innerHTML = "Could not pull data. Make sure you've <a href='https://github.com/geektrainer/aswa-starter/docs/add-database.md'>configured the database</a>."
document.getElementById('task-list').appendChild(messageElement);
}
}
// Displays all tasks on page (called on load)
function displayTasks() {
// Loop through all tasks in the local array
for (let task of tasks) {
// Call helper function to add to UI
addTaskToDisplay(task);
}
}
// Helper function to add task to UI
function addTaskToDisplay(task) {
// Create li element to store task display
const taskElement = document.createElement('li');
// create checkbox
const taskCompleteCheckbox = document.createElement('input');
taskCompleteCheckbox.type = 'checkbox';
// Set the ID of the checkbox to the id of the task
taskCompleteCheckbox.id = task._id;
// Set the checked property to the completion status of the task
// (Completed tasks will be checked)
taskCompleteCheckbox.checked = task.completed;
// Add event handler to each item for clicking on the checkbox
// When the checkbox is clicked, we will make a server call to toggle completed
taskCompleteCheckbox.addEventListener('change', updateTask);
// Add checkbox to li created earlier
taskElement.appendChild(taskCompleteCheckbox);
// Create label for the task
const taskLabel = document.createElement('label');
// Set the for attribute so the checkbox is toggled when the label is clicked
taskLabel.setAttribute('for', task._id);
// Set the text to the title of the task
taskLabel.innerText = task.title;
// Set the completed CSS class if the task is completed
taskLabel.className = task.completed ? 'completed' : '';
// Add the label to the end of the li element
taskElement.appendChild(taskLabel);
// Get the ul element from the page
const taskListElement = document.getElementById('task-list');
// Add the new task to the list on the page
taskListElement.appendChild(taskElement);
}
// Event listener for checkbox change
async function updateTask(e) {
// Get the ID of the task
const taskId = e.target.id;
// Find the task from the array
const task = tasks.find(t => t._id === taskId);
// If no task is found (shouldn't happen), just return
if (!task) return;
// Toggle completed status
task.completed = !task.completed;
// Get the label for the task, which contains the string
// We find this by using nextSibling
// It is next to the checkbox, which is in e.target
const taskLabel = e.target.nextSibling;
// Set the class for the task based on completed status
taskLabel.className = task.completed ? 'completed' : '';
// Call the server to save the changes
await fetch(
`/api/tasks/${taskId}`, // URL of the API
{
method: 'PUT', // method to modify items
body: JSON.stringify(task), // put task in body
headers: {
'Content-Type': 'application/json' // indicate return type of JSON
}
}
);
}
// Event listener for new tasks being created
document.getElementById('task-register').addEventListener('click', async () => {
// Create task by retrieving text from textbox
const task = {
title: document.getElementById('task-title').value
};
// Call server
const response = await fetch(
'/api/tasks', // API location
{
method: 'POST', // POST to create new item
body: JSON.stringify(task), // Add task to body
headers: {
'Content-Type': 'application/json' // Set return type to JSON
}
}
);
// Get the task returned from the server (it will have a new id)
const loadedTask = await response.json();
// Add the task to the array for local storage
tasks.push(loadedTask);
// Add the new task to the display
addTaskToDisplay(loadedTask);
});