Skip to content

Commit 8eb8830

Browse files
rh-maxjrangelramos
andcommitted
Add docs on developing TypeScript functions
Fix section ID Move TypeScript context object reference to the proper guide Fix function header Co-authored-by: Lance Ball <[email protected]> Fix another function header Co-authored-by: Lance Ball <[email protected]> Improve wording Co-authored-by: Lance Ball <[email protected]> Fix yet another function header Co-authored-by: Lance Ball <[email protected]> Fix broken list Add the step of installing dependencies Substitute curl commands with kn func emit commands Improve kn func emit examples Several stylistic improvements Make paragraph more precise Co-authored-by: jrangelramos <[email protected]> Add a comma Use updated TypeScript code examples Co-authored-by: Lance Ball <[email protected]> Improvements to TypeScript code Improve TypeScript code Co-authored-by: jrangelramos <[email protected]>
1 parent e705714 commit 8eb8830

8 files changed

+429
-0
lines changed

_topic_map.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3122,6 +3122,8 @@ Topics:
31223122
File: serverless-functions-getting-started
31233123
- Name: Developing Node.js functions
31243124
File: serverless-developing-nodejs-functions
3125+
- Name: Developing TypeScript functions
3126+
File: serverless-developing-typescript-functions
31253127
- Name: Developing Golang functions
31263128
File: serverless-developing-go-functions
31273129
- Name: Developing Python functions
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
[id="serverless-testing-typescript-functions_{context}"]
2+
= Testing TypeScript functions
3+
4+
TypeScript functions can be tested locally on your computer. In the default project that is created when you create a function using `kn func create`, there is a *test* folder that contains some simple unit and integration tests.
5+
6+
.Procedure
7+
8+
. If you have not previously run tests, install the dependencies first:
9+
+
10+
[source,terminal]
11+
----
12+
$ npm install
13+
----
14+
15+
. Run the tests:
16+
+
17+
[source,terminal]
18+
----
19+
$ npm test
20+
----
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
[id="serverless-typescript-context-object-reference_{context}"]
2+
= TypeScript context object reference
3+
4+
The `context` object has several properties that can be accessed by the function developer.
5+
6+
[id="serverless-typescript-context-object-reference-log_{context}"]
7+
== log
8+
9+
Provides a logging object that can be used to write output to the cluster logs. The log adheres to the link:https://getpino.io/#/docs/api[Pino logging API].
10+
11+
.Example log
12+
[source,javascript]
13+
----
14+
export function handle(context: Context): string {
15+
// log the incoming request body's 'hello' parameter
16+
if (context.body) {
17+
context.log.info((context.body as Record<string, string>).hello);
18+
} else {
19+
context.log.info('No data received');
20+
}
21+
return 'OK';
22+
}
23+
24+
----
25+
26+
You can access the function by using the `kn func emit` command to invoke it:
27+
28+
.Example command
29+
[source,terminal]
30+
----
31+
$ kn func emit --sink 'http://example.function.com'
32+
----
33+
34+
.Example output
35+
[source,terminal]
36+
----
37+
{"level":30,"time":1604511655265,"pid":3430203,"hostname":"localhost.localdomain","reqId":1,"msg":"Processing customer"}
38+
----
39+
40+
You can change the log level to one of `fatal`, `error`, `warn`, `info`, `debug`, `trace`, or `silent`. To do that, change the value of `logLevel` by assigning one of these values to the environment variable `FUNC_LOG_LEVEL` using the `config` command.
41+
42+
[id="serverless-typescript-context-object-reference-query_{context}"]
43+
== query
44+
45+
Returns the query string for the request, if any, as key-value pairs. These attributes are also found on the context object itself.
46+
47+
.Example query
48+
[source,javascript]
49+
----
50+
export function handle(context: Context): string {
51+
// log the 'name' query parameter
52+
if (context.query) {
53+
context.log.info((context.query as Record<string, string>).name);
54+
} else {
55+
context.log.info('No data received');
56+
}
57+
return 'OK';
58+
}
59+
60+
----
61+
62+
You can access the function by using the `kn func emit` command to invoke it:
63+
64+
.Example command
65+
[source,terminal]
66+
----
67+
$ kn func emit --sink 'http://example.function.com' --data '{"name": "tiger"}'
68+
----
69+
70+
.Example output
71+
[source,terminal]
72+
----
73+
{"level":30,"time":1604511655265,"pid":3430203,"hostname":"localhost.localdomain","reqId":1,"msg":"tiger"}
74+
{"level":30,"time":1604511655265,"pid":3430203,"hostname":"localhost.localdomain","reqId":1,"msg":"tiger"}
75+
----
76+
77+
[id="serverless-typescript-context-object-reference-body_{context}"]
78+
== body
79+
80+
Returns the request body, if any. If the request body contains JSON code, this will be parsed so that the attributes are directly available.
81+
82+
.Example body
83+
[source,javascript]
84+
----
85+
export function handle(context: Context): string {
86+
// log the incoming request body's 'hello' parameter
87+
if (context.body) {
88+
context.log.info((context.body as Record<string, string>).hello);
89+
} else {
90+
context.log.info('No data received');
91+
}
92+
return 'OK';
93+
}
94+
----
95+
96+
You can access the function by using the `kn func emit` command to invoke it:
97+
98+
.Example command
99+
[source,terminal]
100+
----
101+
$ kn func emit --sink 'http://example.function.com' --data '{"hello": "world"}'
102+
----
103+
104+
.Example output
105+
[source,terminal]
106+
----
107+
{"level":30,"time":1604511655265,"pid":3430203,"hostname":"localhost.localdomain","reqId":1,"msg":"world"}
108+
----
109+
110+
[id="serverless-typescript-context-object-reference-headers_{context}"]
111+
== headers
112+
113+
Returns the HTTP request headers as an object.
114+
115+
.Example header
116+
[source,javascript]
117+
----
118+
export function handle(context: Context): string {
119+
// log the incoming request body's 'hello' parameter
120+
if (context.body) {
121+
context.log.info((context.headers as Record<string, string>)['custom-header']);
122+
} else {
123+
context.log.info('No data received');
124+
}
125+
return 'OK';
126+
}
127+
----
128+
129+
You can access the function by using the `curl` command to invoke it:
130+
131+
.Example command
132+
[source,terminal]
133+
----
134+
$ curl -H'x-custom-header: some-value’' http://example.function.com
135+
----
136+
137+
.Example output
138+
[source,terminal]
139+
----
140+
{"level":30,"time":1604511655265,"pid":3430203,"hostname":"localhost.localdomain","reqId":1,"msg":"some-value"}
141+
----
142+
143+
[id="serverless-typescript-context-object-reference-http-requests_{context}"]
144+
== HTTP requests
145+
146+
method:: Returns the HTTP request method as a string.
147+
httpVersion:: Returns the HTTP version as a string.
148+
httpVersionMajor:: Returns the HTTP major version number as a string.
149+
httpVersionMinor:: Returns the HTTP minor version number as a string.
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
[id="serverless-typescript-function-return-values_{context}"]
2+
= TypeScript function return values
3+
4+
Functions can return any valid JavaScript type or can have no return value. When a function has no return value specified, and no failure is indicated, the caller receives a `204 No Content` response.
5+
6+
Functions can also return a CloudEvent or a `Message` object in order to push events into the Knative Eventing system. In this case, the developer is not required to understand or implement the CloudEvent messaging specification. Headers and other relevant information from the returned values are extracted and sent with the response.
7+
8+
.Example
9+
[source,javascript]
10+
----
11+
export const handle: Invokable = function (
12+
context: Context,
13+
cloudevent?: CloudEvent
14+
): Message {
15+
// process customer and return a new CloudEvent
16+
const customer = cloudevent.data;
17+
return HTTP.binary(
18+
new CloudEvent({
19+
source: 'customer.processor',
20+
type: 'customer.processed'
21+
})
22+
);
23+
};
24+
----
25+
26+
[id="serverless-typescript-function-return-values-headers_{context}"]
27+
== Returning headers
28+
29+
You can set a response header by adding a `headers` property to the `return` object. These headers are extracted and sent with the response to the caller.
30+
31+
.Example response header
32+
[source,javascript]
33+
----
34+
export function handle(context: Context, cloudevent?: CloudEvent): Record<string, any> {
35+
// process customer and return custom headers
36+
const customer = cloudevent.data as Record<string, any>;
37+
return { headers: { 'customer-id': customer.id } };
38+
}
39+
----
40+
41+
[id="serverless-typescript-function-return-values-status-codes_{context}"]
42+
== Returning status codes
43+
44+
You can set a status code that is returned to the caller by adding a `statusCode` property to the `return` object:
45+
46+
.Example status code
47+
[source,javascript]
48+
----
49+
export function handle(context: Context, cloudevent?: CloudEvent): Record<string, any> {
50+
// process customer
51+
const customer = cloudevent.data as Record<string, any>;
52+
if (customer.restricted) {
53+
return {
54+
statusCode: 451
55+
}
56+
}
57+
// business logic, then
58+
return {
59+
statusCode: 240
60+
}
61+
}
62+
----
63+
64+
Status codes can also be set for errors that are created and thrown by the function:
65+
66+
.Example error status code
67+
[source,javascript]
68+
----
69+
export function handle(context: Context, cloudevent?: CloudEvent): Record<string, string> {
70+
// process customer
71+
const customer = cloudevent.data as Record<string, any>;
72+
if (customer.restricted) {
73+
const err = new Error(‘Unavailable for legal reasons’);
74+
err.statusCode = 451;
75+
throw err;
76+
}
77+
}
78+
----
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
[id="serverless-typescript-functions-context-objects_{context}"]
2+
= TypeScript context objects
3+
4+
Functions are invoked with a `context` object as the first parameter.
5+
6+
.Example context object
7+
[source,javascript]
8+
----
9+
function handle(context:Context): string
10+
----
11+
12+
This object provides access to the incoming HTTP request information, including the HTTP request method, any query strings or headers sent with the request, the HTTP version, and the request body. Incoming requests that contain a CloudEvent attach the incoming instance of the CloudEvent to the context object so that it can be accessed by using `context.cloudevent`.
13+
14+
[id="serverless-typescript-functions-context-objects-methods_{context}"]
15+
== Context object methods
16+
17+
The `context` object has a single method, `cloudEventResponse()`, that accepts a data value and returns a CloudEvent.
18+
19+
In a Knative system, if a function deployed as a service is invoked by an event broker sending a CloudEvent, the broker examines the response. If the response is a CloudEvent, this event is handled by the broker.
20+
21+
.Example context object method
22+
[source,javascript]
23+
----
24+
// Expects to receive a CloudEvent with customer data
25+
export function handle(context: Context, cloudevent?: CloudEvent): CloudEvent {
26+
// process the customer
27+
const customer = cloudevent.data;
28+
const processed = processCustomer(customer);
29+
return context.cloudEventResponse(customer)
30+
.source('/customer/process')
31+
.type('customer.processed')
32+
.response();
33+
}
34+
----
35+
36+
[id="serverless-typescript-functions-context-types_{context}"]
37+
== Context types
38+
39+
The TypeScript type definition files export the following types for use in your functions.
40+
41+
.Exported type definitions
42+
[source,javascript]
43+
----
44+
// Invokable is the expeted Function signature for user functions
45+
export interface Invokable {
46+
(context: Context, cloudevent?: CloudEvent): any
47+
}
48+
49+
// Logger can be used for structural logging to the console
50+
export interface Logger {
51+
debug: (msg: any) => void,
52+
info: (msg: any) => void,
53+
warn: (msg: any) => void,
54+
error: (msg: any) => void,
55+
fatal: (msg: any) => void,
56+
trace: (msg: any) => void,
57+
}
58+
59+
// Context represents the function invocation context, and provides
60+
// access to the event itself as well as raw HTTP objects.
61+
export interface Context {
62+
log: Logger;
63+
req: IncomingMessage;
64+
query?: Record<string, any>;
65+
body?: Record<string, any>|string;
66+
method: string;
67+
headers: IncomingHttpHeaders;
68+
httpVersion: string;
69+
httpVersionMajor: number;
70+
httpVersionMinor: number;
71+
cloudevent: CloudEvent;
72+
cloudEventResponse(data: string|object): CloudEventResponse;
73+
}
74+
75+
// CloudEventResponse is a convenience class used to create
76+
// CloudEvents on function returns
77+
export interface CloudEventResponse {
78+
id(id: string): CloudEventResponse;
79+
source(source: string): CloudEventResponse;
80+
type(type: string): CloudEventResponse;
81+
version(version: string): CloudEventResponse;
82+
response(): CloudEvent;
83+
}
84+
----
85+
86+
[id="serverless-typescript-functions-context-objects-cloudevent-data_{context}"]
87+
== CloudEvent data
88+
89+
If the incoming request is a CloudEvent, any data associated with the CloudEvent is extracted from the event and provided as a second parameter. For example, if a CloudEvent is received that contains a JSON string in its data property that is similar to the following:
90+
91+
[source,json]
92+
----
93+
{
94+
"customerId": "0123456",
95+
"productId": "6543210"
96+
}
97+
----
98+
99+
When invoked, the second parameter to the function, after the `context` object, will be a JavaScript object that has `customerId` and `productId` properties.
100+
101+
.Example signature
102+
[source,javascript]
103+
----
104+
function handle(context: Context, cloudevent?: CloudEvent): CloudEvent
105+
----
106+
107+
The `cloudevent` parameter in this example is a JavaScript object that contains the `customerId` and `productId` properties.

0 commit comments

Comments
 (0)