Add streaming text example

Signed-off-by: Mihovil Ilakovac <mihovil@ilakovac.com>
This commit is contained in:
Mihovil Ilakovac 2023-09-28 11:10:04 +02:00
parent 20eb9e007a
commit 5a33d3c91b
12 changed files with 343 additions and 0 deletions

11
examples/streaming/.gitignore vendored Normal file
View File

@ -0,0 +1,11 @@
/.wasp/
# We ignore env files recognized and used by Wasp.
.env.server
.env.client
# To be extra safe, we by default ignore any files with `.env` extension in them.
# If this is too agressive for you, consider allowing specific files with `!` operator,
# or modify/delete these two lines.
*.env
*.env.*

View File

@ -0,0 +1 @@
File marking the root of Wasp project.

View File

@ -0,0 +1,21 @@
app streaming {
wasp: {
version: "^0.11.5"
},
title: "streaming"
}
route RootRoute { path: "/", to: MainPage }
page MainPage {
component: import Main from "@client/MainPage.jsx"
}
api streamingText {
httpRoute: (GET, "/api/streaming-test"),
fn: import { getText } from "@server/streaming.js",
}
apiNamespace defaultMiddleware {
path: "/api",
middlewareConfigFn: import { getMiddlewareConfig } from "@server/streaming.js",
}

View File

@ -0,0 +1,3 @@
# Ignore editor tmp files
**/*~
**/#*#

View File

@ -0,0 +1,89 @@
* {
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
box-sizing: border-box;
}
body {
margin: 0;
padding: 0;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
"Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
sans-serif;
}
.container {
min-height: 100vh;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
main {
padding: 5rem 0;
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
main p {
font-size: 1.2rem;
}
.logo {
margin-bottom: 2rem;
}
.logo img {
max-height: 200px;
}
.welcome-title {
font-weight: 500;
}
.welcome-subtitle {
font-weight: 400;
margin-bottom: 3rem;
}
.buttons {
display: flex;
flex-direction: row;
}
.buttons .button:not(:last-child) {
margin-right: 0.5rem;
}
.button {
border-radius: 3px;
font-size: 1.2rem;
padding: 1rem 2rem;
text-align: center;
font-weight: 700;
text-decoration: none;
}
.button-filled {
border: 2px solid #bf9900;
background-color: #bf9900;
color: #f4f4f4;
}
.button-outline {
border: 2px solid #8a9cff;
color: #8a9cff;
background-color: none;
}
code {
border-radius: 5px;
padding: 0.2rem;
background: #efefef;
font-family: Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono,
Bitstream Vera Sans Mono, Courier New, monospace;
}

View File

@ -0,0 +1,57 @@
import { useState, useEffect } from "react";
import config from "@wasp/config";
import "./Main.css";
const MainPage = () => {
const { response } = useTextStream("/api/streaming-test");
return (
<div className="container">
<main>
<h1>Streaming Demo</h1>
<p
style={{
maxWidth: "600px",
}}
>
{response}
</p>
</main>
</div>
);
};
export default MainPage;
function useTextStream(path) {
const [response, setResponse] = useState("");
useEffect(() => {
const controller = new AbortController();
fetchStream(
path,
(chunk) => {
setResponse((prev) => prev + chunk);
},
controller
);
return () => {
controller.abort();
};
}, []);
return {
response,
};
}
async function fetchStream(path, onData, controller) {
const response = await fetch(config.apiUrl + path, {
signal: controller.signal,
});
const reader = response.body.pipeThrough(new TextDecoderStream()).getReader();
while (true) {
const { done, value } = await reader.read();
if (done) {
return;
}
onData(value.toString());
}
}

View File

@ -0,0 +1,55 @@
// =============================== IMPORTANT =================================
//
// This file is only used for Wasp IDE support. You can change it to configure
// your IDE checks, but none of these options will affect the TypeScript
// compiler. Proper TS compiler configuration in Wasp is coming soon :)
{
"compilerOptions": {
// JSX support
"jsx": "preserve",
"strict": true,
// Allow default imports.
"esModuleInterop": true,
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
// Wasp needs the following settings enable IDE support in your source
// files. Editing them might break features like import autocompletion and
// definition lookup. Don't change them unless you know what you're doing.
//
// The relative path to the generated web app's root directory. This must be
// set to define the "paths" option.
"baseUrl": "../../.wasp/out/web-app/",
"paths": {
// Resolve all "@wasp" imports to the generated source code.
"@wasp/*": [
"src/*"
],
// Resolve all non-relative imports to the correct node module. Source:
// https://www.typescriptlang.org/docs/handbook/module-resolution.html#path-mapping
"*": [
// Start by looking for the definiton inside the node modules root
// directory...
"node_modules/*",
// ... If that fails, try to find it inside definitely-typed type
// definitions.
"node_modules/@types/*"
]
},
// Correctly resolve types: https://www.typescriptlang.org/tsconfig#typeRoots
"typeRoots": [
"../../.wasp/out/web-app/node_modules/@types"
],
// Since this TS config is used only for IDE support and not for
// compilation, the following directory doesn't exist. We need to specify
// it to prevent this error:
// https://stackoverflow.com/questions/42609768/typescript-error-cannot-write-file-because-it-would-overwrite-input-file
"outDir": "phantom"
},
"exclude": [
"phantom"
],
}

View File

@ -0,0 +1 @@
/// <reference types="../../.wasp/out/web-app/node_modules/vite/client" />

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

View File

@ -0,0 +1,29 @@
import { StreamingText } from "@wasp/apis/types";
import { MiddlewareConfigFn } from "@wasp/middleware";
// Custom API endpoint that returns a streaming text.
export const getText: StreamingText = async (req, res, context) => {
res.setHeader("Content-Type", "text/html; charset=utf-8");
res.setHeader("Transfer-Encoding", "chunked");
let counter = 1;
res.write("Hm, let me see...\n");
while (counter <= 10) {
// Send a chunk of data.
if (counter === 10) {
res.write(`and finally about ${counter}.`);
} else {
res.write(`let's talk about number ${counter} and `);
}
counter++;
// Wait for 1 second.
await new Promise((resolve) => setTimeout(resolve, 1000));
}
// End the response.
res.end();
};
// Returning the default config.
export const getMiddlewareConfig: MiddlewareConfigFn = (config) => {
return config;
};

View File

@ -0,0 +1,48 @@
// =============================== IMPORTANT =================================
//
// This file is only used for Wasp IDE support. You can change it to configure
// your IDE checks, but none of these options will affect the TypeScript
// compiler. Proper TS compiler configuration in Wasp is coming soon :)
{
"compilerOptions": {
// Allows default imports.
"esModuleInterop": true,
"allowJs": true,
"strict": true,
// Wasp needs the following settings enable IDE support in your source
// files. Editing them might break features like import autocompletion and
// definition lookup. Don't change them unless you know what you're doing.
//
// The relative path to the generated web app's root directory. This must be
// set to define the "paths" option.
"baseUrl": "../../.wasp/out/server/",
"paths": {
// Resolve all "@wasp" imports to the generated source code.
"@wasp/*": [
"src/*"
],
// Resolve all non-relative imports to the correct node module. Source:
// https://www.typescriptlang.org/docs/handbook/module-resolution.html#path-mapping
"*": [
// Start by looking for the definiton inside the node modules root
// directory...
"node_modules/*",
// ... If that fails, try to find it inside definitely-typed type
// definitions.
"node_modules/@types/*"
]
},
// Correctly resolve types: https://www.typescriptlang.org/tsconfig#typeRoots
"typeRoots": [
"../../.wasp/out/server/node_modules/@types"
],
// Since this TS config is used only for IDE support and not for
// compilation, the following directory doesn't exist. We need to specify
// it to prevent this error:
// https://stackoverflow.com/questions/42609768/typescript-error-cannot-write-file-because-it-would-overwrite-input-file
"outDir": "phantom",
},
"exclude": [
"phantom"
],
}

View File

@ -0,0 +1,28 @@
{
"compilerOptions": {
// Enable default imports in TypeScript.
"esModuleInterop": true,
"allowJs": true,
// The following settings enable IDE support in user-provided source files.
// Editing them might break features like import autocompletion and
// definition lookup. Don't change them unless you know what you're doing.
//
// The relative path to the generated web app's root directory. This must be
// set to define the "paths" option.
"baseUrl": "../../.wasp/out/server/",
"paths": {
// Resolve all non-relative imports to the correct node module. Source:
// https://www.typescriptlang.org/docs/handbook/module-resolution.html#path-mapping
"*": [
// Start by looking for the definiton inside the node modules root
// directory...
"node_modules/*",
// ... If that fails, try to find it inside definitely-typed type
// definitions.
"node_modules/@types/*"
]
},
// Correctly resolve types: https://www.typescriptlang.org/tsconfig#typeRoots
"typeRoots": ["../../.wasp/out/server/node_modules/@types"]
}
}