Public
Edited
Dec 20, 2023
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
{
await visibility();

// Constants
const endpointKey = "EXAMPLE_1";

// Step 1: Create a local function returning files when a new HTTP request arrives:
const httpHandler = async (request) => {
return new Response("Hello World!");
};

// Step 2: Start a ServiceWorker and open a MessageChannel used to recieve HTTP requests and send responses:
const {
port,
baseUrl,
close: closeChannel
} = await WebRunHttp.newRemoteRelayChannel();
invalidation.then(closeChannel);
const serviceUrl = new URL(`~${endpointKey}/`, baseUrl);

// Step 3: Instantiate a "server" using the communcation channel with the ServiceWorker:
const close = await WebRunHttp.initHttpService(httpHandler, {
key: endpointKey,
port
});
invalidation.then(close);

const endpointUrl = `${serviceUrl}/index.html`;
const label = `/~${endpointKey}/index.html`;
return renderIframe(endpointUrl, label);
}
Insert cell
Insert cell
Insert cell
{
// await visibility();

// Name of the second endpoint
const endpointKey = "ENDPOINT_EXAMPLE_2";

// -----------------
// Start the ServiceWorker and open a communication port:
const {
port,
baseUrl,
close: closeChannel
} = await WebRunHttp.newRemoteRelayChannel();
invalidation.then(closeChannel);

// -----------------
// Define the base URL for this service:
const serviceBaseUrl = `${baseUrl}~${endpointKey}`;

// -----------------
// Define an HTTP handler. In this case it is a simple file dispatcher
// returning file content by path.
const httpHandler = async (request) => {
const url = request.url;

// To get the path from URL we need to remove the service prefix:
const path = url.slice(serviceBaseUrl.length);

// Get the file by the corresponding path:
const file = files[path];

// Send 404 if nothing was found:
if (!file) {
return new Response(`Error 404: File Not Found. Path: '${path}'.`, {
status: 404,
statusText: "File not found"
});
}

// Return the file content with the corresponding MIME type:
return new Response(file.content, {
headers: {
status: 200,
"Content-Type": file.mimeType
}
});
};

// -----------------
// Register a new HTTP service using the communication port defined above:
const close = await WebRunHttp.initHttpService(httpHandler, {
key: endpointKey,
port
});
invalidation.then(close);

// -----------------
// And finally we can create an iframe element visualizing the page:
const endpointUrl = `${serviceBaseUrl}/index.html`;
const label = `/~${endpointKey}/index.html`;
return renderIframe(endpointUrl, label);
}
Insert cell
Insert cell
{
await visibility();

// Name of the second endpoint
const endpointKey = "ENDPOINT_EXAMPLE_3";

// -----------------
// Start the ServiceWorker and open a communication port:
const {
port,
baseUrl,
close: closeChannel
} = await WebRunHttp.newRemoteRelayChannel();
invalidation.then(closeChannel);

// -----------------
// Define the base URL for this service:
const serviceBaseUrl = `${baseUrl}~${endpointKey}`;

// -----------------
// This "database" is used to store submitted data:
let data = {
firstName: "John",
lastName: "Smith"
};
const div = htl.html`<div>`;
function updateDiv() {
div.innerText = JSON.stringify(data);
}
updateDiv();
// -----------------
// Request dispatcher.
const httpHandler = async (request) => {
const { url, method } = request;
const path = url.slice(serviceBaseUrl.length);
if (method === "POST") {
const f = await request.formData();
for (const [name, value] of f.entries()) {
data[name] = value;
}
updateDiv();
return replyWithHtmlPage(`
<h3>Success!</h3>
<p>Recieved data:</p>
<pre>${JSON.stringify(data, null, 2)}</pre>
<p>Return <a href="./index.html">back</a>.</p>
`);
} else if (method === "GET") {
return replyWithHtmlPage(`
<h3>Submit Form</h3>

<form action="./post" method="post" enctype="multipart/form-data">
<div>
<label>
First Name:
<input name="firstName" type="text" value=${JSON.stringify(
data.firstName
)}></input>
</label>
</div>
<div>
<label>
Last Name:
<input name="lastName" type="text" value=${JSON.stringify(
data.lastName
)}></input>
</label>
</div>
<input type="submit" value="Go!"></input>
</form>

<p>
Click <a href="./index.html">here to refresh this page</a>.
</p>

`);
}
};

const close = await WebRunHttp.initHttpService(httpHandler, {
key: endpointKey,
port
});
invalidation.then(close);

const endpointUrl = `${serviceBaseUrl}/index.html`;
const label = `/~${endpointKey}/index.html`;
const iframe = renderIframe(endpointUrl, label);

return htl.html`<div>${div}${iframe}`;

// --------------------

function replyWithHtmlPage(content) {
return new Response(
`<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
</head>
<body>
${content}
</body>
</html>
`,
{
status: 200,
headers: {
"Content-Type": "text/html"
}
}
);
}
}
Insert cell
function renderIframe(url, label) {
return htl.html`<div>
<iframe src="${url}" width="100%" height="250" style="border: 1px solid gray; outline: none; width: 100%; height: 300px;"></iframe>
</div>`;
}
Insert cell
Insert cell
WebRunHttp = import(
"https://unpkg.com/@statewalker/webrun-http-browser@0.3/dist/index.js"
)
Insert cell
import { toc } from "@nebrius/indented-toc"
Insert cell

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more