Shaokang's Blog

Cloudflare worker is a serverless application based on v9 engine. The structure is a little bit different than the Node.js. Endpoints is familiar to most people in developing. But this seems to be different on worker. And it is useful especially when different services is running in the same worker. This one will show how to integrate traditional endpoint feature on cloudflare worker to likely have a fake endpoint handler. This is a sub-project of Financial website.

End-points:

Three end points is in this sample, they are arranged in three different purpose.

Check the AES Encrypt and Decrypt on Cloudflare worker to see the encryption and decryption methods using in /authenticate and /generate, which is based on AES-GCM

/authenticate return a password if the information, like token and quizepartcode matched with our record. Otherwise, return false;

/generate Generate a user learning progress, will need quizpartcode in the body.

/sendmail with a password as authentication to send email via sendgrid from [email protected]. This method is not enabled as sendgrid has been blocked by a lot of client.

Initial Handling

In each fetch request to worker, there will be a request with content pass to the worker, usually and officially returning method is

1
2
3
addEventListener('fetch', event => {
event.respondWith(handleRequest(event.request))
})

Reroute Request

Even though Worker is just JavaScript running on v8, we could just create a function to reroute each request based on the address user is requesting, like

1
2
3
4
5
(endpoint, fn) => {
url = new URL(request.url);
if (url.pathname === endpoint && request.method === "POST")
return fn(request);
return null;

In javascript, functions could wrapped using a variable, so, it is possible to create an object to contain everything, like

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const app = {
get: (endpoint, fn) => { // for get request
url = new URL(request.url);
if (url.pathname === endpoint && request.method === "GET")
return fn(request);
return null;
},
post: (endpoint, fn) => {
url = new URL(request.url);
if (url.pathname === endpoint && request.method === "POST")
return fn(request);
return null;
}
};

Return a Response

Whenever needed in handleRequest, it is as easy as calling app.post("/endPoint", functionName). And if null is returned, check next endpoint, otherwise the correct function will be called and executed. The reroute function like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37

/**
* reroute
* @param {Request} request
*/
async function handleRequest(request) {
const app = {
get: (endpoint, fn) => { // for get request
url = new URL(request.url);
if (url.pathname === endpoint && request.method === "GET")
return fn(request);
return null;
},
post: (endpoint, fn) => {
url = new URL(request.url);
if (url.pathname === endpoint && request.method === "POST")
return fn(request);
return null;
}
};
let lastResponse = app.post("/authenticate", handleAuthen)
if (lastResponse) {
return lastResponse;
}

lastResponse = app.post("/generate", handleGen)
if (lastResponse) {
return lastResponse;
}

lastResponse = app.post("/sendmail", sendMail)
if (lastResponse) {
return lastResponse;
}

return new Response('{"message":"Hello! Default is touched."}', notFoundHead);
}

notFoundHead is the head content created as a js Object, like

1
2
3
4
5
6
7
8
9
let notFoundHead = {
status: '404',
statusText: 'NOT FOUND',
headers: {
'Content-Type': 'application/json;charset=utf-8',
'Access-Control-Allow-Origin': 'https://capitaltwo.ga',
'Access-Control-Allow-Methods': 'GET,POST,PUT,DELETE'
}
}

Caution: Without a head containing the Access-Control-Allow-Origin, CORS error will be posted. Cloudflare worker seems to be strict on this thing. If any error or exception happened without any trycatch, CORS will also be given. So, to be convenient in debugging, a good practice is always to return a Not found header when error happened in some cases.

If method other than GET is used, Access-Control-Allow-Methods should also be used. Otherwise, CORS error would also happen.

And when posting fetch request, do not append header in this example because it is not appropriately handled and it might have problem in this case.

One function in the sample is handleAuthen, and one another is handleGen. Those two are normal functions and just return a new Response at last would be fine. Like:

1
2
3
4
5
6
7
8
9
/**
*
* @param {*} request
*/
async function handleAuthen(request) {
var data = await request.json();
return new Response(JSON.stringify({
XXX: XXX}), Header)
}

Visiting Data part in request

Because the data section of the request is a Promise object, it has to be called with await and .json() in order to visit it. Like: var data = await request.json();.

SendMail through sendgrid

In the real case, sending a mail to the user is general. And sometimes, we have certain sensitive data that should not be promoted to client. This could be implemented through the following method in worker and called in client like this. That is what the third endpoint /sendmail is doing. like:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
/**
*
* @param {*} request
*/
async function sendMail(request) {
let a = await fetch("https://api.sendgrid.com/v3/mail/send", {
method: "POST",
headers: {
"Authorization": "Bearer SG.P_Xcg7PmRgirESQCCXXX...Tm1xo7tz5w",
"Content-Type": "application/json"
},
body: JSON.stringify({
"personalizations": [
{
"to": [
{
"email": "[email protected]"
}
]
}
],
"from": {
"email": "[email protected]"
},
"subject": "Sending with SendGrid is Fun",
"content": [
{
"type": "text/plain",
"value": "and easy to do anywhere, even with cURL"
}
]
})
});

if (a.ok) {
return new Response('{"message":"successfully sent"}', normalHeader)
} else {
let k = await a.json();
return new Response(JSON.stringify(k), normalHeader)
}
}

Caution: sendgrid will not return anything if the response status is ok, which could be checked with a.ok in the previous sample. If wrong, a data will be returned and could be decoded to json Object

Run

In order to run and test locally, you need to install Cloudflare Worker Local client first.

Then do:

1
wrangler dev

 Comments