Skip to content

Commit f679270

Browse files
authored
update spin 3 docs to represent js sdk v3 structure (#1472)
* update spin 3 docs to represent js sdk v3 structure Signed-off-by: karthik2804 <[email protected]> * address review comments Signed-off-by: karthik2804 <[email protected]> --------- Signed-off-by: karthik2804 <[email protected]>
1 parent 89a4d13 commit f679270

11 files changed

+253
-246
lines changed

content/spin/v3/ai-sentiment-analysis-api-tutorial.md

Lines changed: 13 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -451,16 +451,8 @@ impl FromStr for Sentiment {
451451
{{ startTab "TypeScript"}}
452452

453453
```typescript
454-
import {
455-
HandleRequest,
456-
HttpRequest,
457-
HttpResponse,
458-
Llm,
459-
InferencingModels,
460-
InferencingOptions,
461-
Router,
462-
Kv,
463-
} from "@fermyon/spin-sdk";
454+
import { Llm, Kv } from "@fermyon/spin-sdk";
455+
import { AutoRouter } from 'itty-router';
464456

465457
interface SentimentAnalysisRequest {
466458
sentence: string;
@@ -492,9 +484,9 @@ negative
492484
<SENTENCE>
493485
`;
494486

495-
async function performSentimentAnalysis(request: HttpRequest) {
487+
async function performSentimentAnalysis(request: Request) {
496488
// Parse sentence out of request
497-
let data = request.json() as SentimentAnalysisRequest;
489+
let data = await request.json() as SentimentAnalysisRequest;
498490
let sentence = data.sentence;
499491
console.log("Performing sentiment analysis on: " + sentence);
500492

@@ -515,9 +507,9 @@ async function performSentimentAnalysis(request: HttpRequest) {
515507

516508
// Otherwise, perform sentiment analysis
517509
console.log("Running inference");
518-
let options: InferencingOptions = { maxTokens: 6 };
510+
let options: Llm.InferencingOptions = { maxTokens: 6 };
519511
let inferenceResult = Llm.infer(
520-
InferencingModels.Llama2Chat,
512+
Llm.InferencingModels.Llama2Chat,
521513
PROMPT.replace("<SENTENCE>", sentence),
522514
options
523515
);
@@ -539,36 +531,23 @@ async function performSentimentAnalysis(request: HttpRequest) {
539531
console.log("Caching sentiment in KV store");
540532
kv.set(sentence, sentiment);
541533

542-
return {
543-
status: 200,
544-
body: JSON.stringify({
534+
return new Response(JSON.stringify({
545535
sentiment,
546-
} as SentimentAnalysisResponse),
547-
};
536+
} as SentimentAnalysisResponse), { headers: { "Content-Type": "application/json" }});
548537
}
549538

550-
let router = Router();
539+
let router = AutoRouter();
551540

552541
// Map the route to the handler
553-
router.post("/api/sentiment-analysis", async (_, req) => {
542+
router.post("/api/sentiment-analysis", async (req) => {
554543
console.log(`${new Date().toISOString()} POST /sentiment-analysis`);
555544
return await performSentimentAnalysis(req);
556545
});
557546

558-
// Catch all 404 handler
559-
router.all("/api/*", async (_, req) => {
560-
return {
561-
status: 404,
562-
body: "Not found",
563-
};
547+
//@ts-ignore
548+
addEventListener('fetch', async (event: FetchEvent) => {
549+
event.respondWith(router.fetch(event.request));
564550
});
565-
566-
// Entry point to the Spin handler
567-
export const handleRequest: HandleRequest = async function (
568-
request: HttpRequest
569-
): Promise<HttpResponse> {
570-
return await router.handleRequest(request, request);
571-
};
572551
```
573552

574553
{{ blockEnd }}

content/spin/v3/build.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,12 +78,12 @@ It's normally convenient to put the detailed build instructions in `package.json
7878
```json
7979
{
8080
"scripts": {
81-
"build": "npx webpack --mode=production && npx mkdirp target && npx j2w -i dist.js -d combined-wit -n combined -o target/spin-http-js.wasm"
81+
"build": "knitwit --out-dir build/wit/knitwit --out-world combined && npx webpack --mode=production && npx mkdirp target && npx j2w -i dist.js -d combined-wit -n combined -o target/spin-http-js.wasm"
8282
}
8383
}
8484
```
8585

86-
{{ details "Parts of the build script" "The build script calls out to [`webpack`](https://webpack.js.org/) and `j2w` which is a script provided by the `@fermyon/spin-sdk` package that utilizes [`ComponentizeJS`](https://github.com/bytecodealliance/ComponentizeJS)."}}
86+
{{ details "Parts of the build script" "The build script calls out to [`webpack`](https://webpack.js.org/) and `j2w` which is a script provided by the `@fermyon/spin-sdk` package that utilizes [`ComponentizeJS`](https://github.com/bytecodealliance/ComponentizeJS). [`knitwit`](https://github.com/fermyon/knitwit) is a utility that helps combine `wit` worlds "}}
8787

8888
The build command can then call the NPM script:
8989

content/spin/v3/javascript-components.md

Lines changed: 60 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ url = "https://github.com/fermyon/developer/blob/main/content/spin/v3/javascript
1212
- [HTTP Components](#http-components)
1313
- [Sending Outbound HTTP Requests](#sending-outbound-http-requests)
1414
- [Storing Data in Redis From JS/TS Components](#storing-data-in-redis-from-jsts-components)
15-
- [Routing in a Component](#routing-in-a-component)
1615
- [Storing Data in the Spin Key-Value Store](#storing-data-in-the-spin-key-value-store)
1716
- [Storing Data in SQLite](#storing-data-in-sqlite)
1817
- [Storing Data in MySQL and PostgreSQL Relational Databases](#storing-data-in-mysql-and-postgresql-relational-databases)
@@ -76,13 +75,13 @@ This creates a directory of the following structure:
7675
<!-- @nocpy -->
7776

7877
```text
79-
hello-world/
80-
├── knitwit.json
78+
hello-world
79+
├── config
80+
│ └── knitwit.json
8181
├── package.json
8282
├── spin.toml
8383
├── src
84-
│   ├── index.ts
85-
│   └── spin.ts
84+
│ └── index.ts
8685
├── tsconfig.json
8786
└── webpack.config.js
8887
```
@@ -134,47 +133,53 @@ for writing Spin components with the Spin JS/TS SDK.
134133
> Make sure to read [the page describing the HTTP trigger](./http-trigger.md) for more
135134
> details about building HTTP applications.
136135
137-
Building a Spin HTTP component using the JS/TS SDK means writing a single function
138-
that takes an HTTP request and a Response Builder which can be used to return an HTTP response as a parameter.
136+
Building a Spin HTTP component with the JavaScript/TypeScript SDK now involves adding an event listener for the `fetch` event. This event listener handles incoming HTTP requests and allows you to construct and return HTTP responses.
139137

140138
Below is a complete implementation for such a component in TypeScript:
141139

142140
```javascript
143-
import { ResponseBuilder } from "@fermyon/spin-sdk";
141+
import { AutoRouter } from 'itty-router';
144142

145-
export async function handler(req: Request, res: ResponseBuilder) {
146-
console.log(req);
147-
res.send("hello universe");
148-
}
149-
```
143+
let router = AutoRouter();
144+
145+
router
146+
.get("/", () => new Response("hello universe"))
147+
.get('/hello/:name', ({ name }) => `Hello, ${name}!`)
150148

151-
The important things to note in the implementation above:
149+
//@ts-ignore
150+
addEventListener('fetch', async (event: FetchEvent) => {
151+
event.respondWith(router.fetch(event.request));
152+
});
152153

153-
- The `handler` function is the entry point for the Spin component.
154-
- The execution of the function terminates once `res.send` or `res.end` is called.
154+
```
155155

156156
## Sending Outbound HTTP Requests
157157

158158
If allowed, Spin components can send outbound HTTP requests.
159159
Let's see an example of a component that makes a request to [an API that returns random animal facts](https://random-data-api.fermyon.app/animals/json)
160160

161161
```javascript
162-
import { ResponseBuilder } from "@fermyon/spin-sdk";
163-
164-
interface AnimalFact {
165-
timestamp: number;
166-
fact: string;
167-
}
162+
import { AutoRouter } from 'itty-router';
168163

169-
export async function handler(req: Request, res: ResponseBuilder) {
170-
const animalFactResponse = await fetch("https://random-data-api.fermyon.app/animals/json")
171-
const animalFact = await animalFactResponse.json() as AnimalFact
164+
let router = AutoRouter();
172165

173-
const body = `Here's an animal fact: ${animalFact.fact}\n`
166+
router
167+
.get("*", getDataFromAPI)
174168

175-
res.set({"content-type": "text/plain"})
176-
res.send(body)
169+
async function getDataFromAPI(_request: Request) {
170+
let response = await fetch(
171+
'https://random-data-api.fermyon.app/physics/json',
172+
);
173+
let data = await response.json();
174+
let fact = `Here is a fact: ${data.fact}`;
175+
return new Response(fact);
177176
}
177+
178+
//@ts-ignore
179+
addEventListener('fetch', async (event: FetchEvent) => {
180+
event.respondWith(router.fetch(event.request));
181+
});
182+
178183
```
179184

180185
Before we can execute this component, we need to add the `random-data-api.fermyon.app`
@@ -216,7 +221,7 @@ content-type: application/json; charset=utf-8
216221
content-length: 185
217222
server: spin/0.1.0
218223
219-
Here's an animal fact: Reindeer grow new antlers every year
224+
Here is a fact: Reindeer grow new antlers every year
220225
```
221226

222227
> Without the `allowed_outbound_hosts` field populated properly in `spin.toml`,
@@ -245,31 +250,35 @@ Using the Spin's JS SDK, you can use the Redis key/value store and to publish me
245250
Let's see how we can use the JS/TS SDK to connect to Redis:
246251

247252
```javascript
248-
import { ResponseBuilder, Redis } from '@fermyon/spin-sdk';
253+
import { AutoRouter } from 'itty-router';
254+
import { Redis } from '@fermyon/spin-sdk';
249255

250256
const encoder = new TextEncoder();
251-
const decoder = new TextDecoder();
252257
const redisAddress = 'redis://localhost:6379/';
253258

254-
export async function handler(_req: Request, res: ResponseBuilder) {
255-
try {
256-
let db = Redis.open(redisAddress);
257-
db.set('test', encoder.encode('Hello world'));
258-
let val = db.get('test');
259-
260-
if (!val) {
261-
res.status(404);
262-
res.send();
263-
return;
264-
}
265-
// publish to a channel names "message"
266-
db.publish("message", val)
267-
res.send(val);
268-
} catch (e: any) {
269-
res.status(500);
270-
res.send(`Error: ${JSON.stringify(e.payload)}`);
271-
}
272-
}
259+
let router = AutoRouter();
260+
261+
router
262+
.get("/", () => {
263+
try {
264+
let db = Redis.open(redisAddress);
265+
db.set('test', encoder.encode('Hello world'));
266+
let val = db.get('test');
267+
268+
if (!val) {
269+
return new Response(null, { status: 404 });
270+
}
271+
return new Response(val);
272+
} catch (e: any) {
273+
return new Response(`Error: ${JSON.stringify(e.payload)}`, { status: 500 });
274+
}
275+
})
276+
277+
//@ts-ignore
278+
addEventListener('fetch', async (event: FetchEvent) => {
279+
event.respondWith(router.fetch(event.request));
280+
});
281+
273282
```
274283

275284
This HTTP component demonstrates fetching a value from Redis by key, setting a key with a value, and publishing a message to a Redis channel.
@@ -285,33 +294,6 @@ As with all networking APIs, you must grant access to Redis hosts via the `allow
285294
allowed_outbound_hosts = ["redis://localhost:6379"]
286295
```
287296

288-
## Routing in a Component
289-
290-
The JavaScript/TypeScript SDK provides a router that makes it easier to handle routing within a component. The router is based on [`itty-router`](https://www.npmjs.com/package/itty-router). An additional function `handleRequest` has been implemented in the router to allow passing in the Spin HTTP request directly. An example usage of the router is given below:
291-
292-
```javascript
293-
import { ResponseBuilder, Router } from '@fermyon/spin-sdk';
294-
295-
let router = Router();
296-
297-
router.get("/", (_, req, res) => { handleDefaultRoute(req, res) })
298-
router.get("/home/:id", (metadata, req, res) => { handleHomeRoute(req, res, metadata.params.id) })
299-
300-
async function handleDefaultRoute(_req: Request, res: ResponseBuilder) {
301-
res.set({ "content-type": "text/plain" });
302-
res.send("Hello from default route");
303-
}
304-
305-
async function handleHomeRoute(_req: Request, res: ResponseBuilder, id: string) {
306-
res.set({ "content-type": "text/plain" });
307-
res.send(`Hello from home route with id: ${id}`);
308-
}
309-
310-
export async function handler(req: Request, res: ResponseBuilder) {
311-
await router.handleRequest(req, res);
312-
}
313-
```
314-
315297
## Storing Data in the Spin Key-Value Store
316298

317299
Spin has a key-value store built in. For information about using it from TypeScript/JavaScript, see [the key-value store tutorial](key-value-store-tutorial).
@@ -381,5 +363,5 @@ These are some of the suggested libraries that have been tested and confirmed to
381363

382364
## Caveats
383365

384-
- All `spin-sdk` related functions and methods (like `Variables`, `Redis`, `Mysql`, `Pg`, `Kv` and `Sqlite`) can be called only inside the `handler` function. This includes `fetch`. Any attempts to use it outside the function will lead to an error. This is due to Wizer using only Wasmtime to execute the script at build time, which does not include any Spin SDK support.
366+
- All `spin-sdk` related functions and methods (like `Variables`, `Redis`, `Mysql`, `Pg`, `Kv` and `Sqlite`) can be called only inside the fetch event handler. This includes `fetch`. Any attempts to use it outside the function will lead to an error. This is due to Wizer using only Wasmtime to execute the script at build time, which does not include any Spin SDK support.
385367
- No crypto operation that involve handling private keys are supported.

0 commit comments

Comments
 (0)