Node router for Yandex Cloud Functions
npm install yandex-cloud-functions-routeryandex-cloud-functions-router is a Node router for Yandex Cloud Function platform. It inspired by aws-lambda-router and simplifies the development of serverless applications for Yandex Cloud.* Provides an easy way to create Node-based handlers either for HTTP invocations and standard triggers.
* Supports almost all supported triggers such as Timer trigger, Message Queue trigger, Object Storage trigger, and others.
* Handles CORS for HTTP requests.
* Can filter requests by HTTP method, params, body, queue identifier, and other trigger-specific params.
* Customizable error mapping to appropriate responses.
* Zero external dependencies.
* Supports Typescript out of the box. 🤘
Once you create a Node application that uses this package, you can assign some routes to the handlers. Next, you publish the Yandex Cloud Functions application and it will handle every request with the corresponding handler.
``typescript
import { router } from 'yandex-cloud-functions-router';
export.handler = router({
http: [
{
httpMethod: ['GET'],
handler: (event, context) => {
// Handle HTTP request
return {
statusCode: 200,
body: 'Hello from Yandex Cloud Functions'
};
}
},
{
httpMethod: ['POST'],
handler: (event, context) => {
// Handle HTTP request
return {
statusCode: 200,
body: 'Hello again'
};
}
}
],
message_queue: [
{
queueId: ['a4wt2lnqwvjwnregbqbb'],
handler: (event, context, message) => {
// Handle Message Queue item
}
}
]
});
`
Examples:
* Typescript project
* Javascript project
* Node 12+
1. Install the package using npm:
`bash`
$ npm install yandex-cloud-functions-router
2. Create router handler and export it from the main module (for example, in index.js):`typescript
import { router } from 'yandex-cloud-functions-router';
export.handler = router({ / ... / });
`
3. Create a new Yandex Cloud Function in the developer console. Use nodejs12/nodejs12-preview as a runtime environment. Also, don't forget to specify Entrypoint for the function.
4. Now the function is able to handle requests 🎉
Mark the function as public to be able to make HTTP requests. Once it's done, you can access it by provided URL.
To handle incoming HTTP requests, add http key into the routes definition. httpMethod and handler params are mandatory for every route.
`typescript
import { router } from 'yandex-cloud-functions-router';
export.handler = router({
http: [
{
httpMethod: ['GET'], / Filter by HTTP method (required). /
params: { }, / Filter by Query String params (optional). /
body: { }, / Filter by body content (optional). /
decodeBase64Body: false, / Whether to decode base64 requests. /
validators: { }, / Additional validator(s) for the event. /
handler: (event, context) => { / Handler function (required). /
// Handle HTTP GET request
return {
statusCode: 200
};
}
},
{
httpMethod: ['POST'],
handler: (event, context) => {
// Handle HTTP POST request
return {
statusCode: 200
};
}
}
]
});
`
handler accepts two params that came from Yandex Cloud. The result that function returns passed directly to Yandex Cloud, so please use Yandex Cloud Function response format.
It is possible to filter requests by HTTP method, query params, and body content.
To filter requests by HTTP method, specify httpMethod property for a route. It's an array of HTTP methods that the current handle can process. It is a required property.
` export.handler = router({ return { return { return {Example
typescript
import { router } from 'yandex-cloud-functions-router';
http: [
{
httpMethod: ['GET'],
handler: (event, context) => {
// Handle HTTP GET request
statusCode: 200
};
}
},
{
httpMethod: ['POST'],
handler: (event, context) => {
// Handle HTTP POST request
statusCode: 200
};
}
},
{
httpMethod: ['PUT'],
handler: (event, context) => {
// Handle HTTP PUT request
statusCode: 200
};
}
}
]
});
`
To filter requests by Query String params, specify params property for a route. It's a key-value dictionary (key is param name).
There are few ways how to hanlde these params - by exact match, substring, or regular expression.
It is an optional property.
` export.handler = router({ return { return { return { return {Example
typescript
import { router } from 'yandex-cloud-functions-router';
http: [
{
httpMethod: ['GET'],
params: {
type: {
type: 'exact',
value: 'get'
}
},
handler: (event, context) => {
// Handle /?type=get requests
statusCode: 200
};
}
},
{
httpMethod: ['GET'],
params: {
type: {
type: 'substring',
value: 'qwerty'
}
},
handler: (event, context) => {
// Handle requests where "type" param contains "qwerty"
statusCode: 200
};
}
},
{
httpMethod: ['GET'],
params: {
type: {
type: 'regexp',
pattern: /[0-9]+/i
}
},
handler: (event, context) => {
// Handle requests where "type" param contains numbers
statusCode: 200
};
}
},
{
httpMethod: ['GET'],
params: {
type: {
type: 'exact',
value: 'find'
},
context: {
type: 'exact',
value: '1'
}
},
handler: (event, context) => {
// Handle /?type=find&context=1 requests
statusCode: 200
};
}
}
]
});
`
To filter requests by Body content, specify body property for a route. For the moment, only JSON content is supported. So to use the filter the request must contain Content-Type: application/json header and body should contain valid JSON object. If these criteria aren't met, the route will be ignored. It is an optional property.
` export.handler = router({ return { return {Example
typescript
import { router } from 'yandex-cloud-functions-router';
http: [
{
httpMethod: ['POST'],
body: {
json: {
type: 'add',
},
},
handler: (event, context) => {
// Handle requests with the following body content
// { "type": "add", ... }
statusCode: 200
};
}
},
{
httpMethod: ['POST'],
body: {
json: {
type: 'update',
},
},
handler: (event, context) => {
// Handle requests with the following body content
// { "type": "update", ... }
statusCode: 200
};
}
}
]
});
`
Validators supposed to validate the event's content. In contrast to filtering, the route won't be skipped if the validator fails. Instead, exception will be thrown (and could be handled as a final HTTP 400 error). It is useful to use validators, when you're sure that it's the right route, but want to ensure that incoming request contains the correct data. For instance, if an incoming request doesn't contain the required field, it will fail.
` export.handler = router({ return false; return {Example
typescript
import { router } from 'yandex-cloud-functions-router';
http: [
{
httpMethod: ['POST'],
validators: [
(event, context) => {
if (/ check something in the event /) {
return true;
}
}
],
handler: (event, context) => {
// Here you are sure that the request contains the correct data
statusCode: 200
};
}
}
]
});
`
To handle CORS requests add second param to the router function:
`typescript
import { router } from 'yandex-cloud-functions-router';
export.handler = router({
http: [
{
httpMethod: ['POST'],
handler: (event, context) => {
return {
statusCode: 200
};
}
}
]
},
{
cors: {
enable: true, / Whether CORS support is enabled (required). /
allowedOrigins: ['http://localhost:5000'], / Origins that allowed to request the function (optional). /
allowedMethods: ['GET', 'POST', 'PUT'], / Allowed methods that will be put into Access-Control-Allow-Methods (optional). /
allowedHeaders: ['X-Test'], / Allowed custom headers that will be put into Access-Control-Allow-Headers (optional). /
allowCredentials: true / Whether to add Access-Control-Allow-Credentials to the response (optional). /
}
);
`
To handle Timer trigger events, add the timer key into the routes definition. The only handler param is mandatory for the route.
`typescript
import { router } from 'yandex-cloud-functions-router';
export.handler = router({
timer: [
{
triggerId: ['a4wt2lnqwvjwnregbqbb'], / Filter by trigger identifier (optional). /
handler: (event, context, message) => { / Handler function (required). /
// Handle Timer trigger event
}
}
]
});
`
handler accepts three params that came from Yandex Cloud.
It is possible to filter events by trigger ID.
To filter events by Trigger ID, specify triggerId property for a route. It is an optional property.
` export.handler = router({Example
typescript
import { router } from 'yandex-cloud-functions-router';
timer: [
{
triggerId: ['a4wt2lnqwvjwnregbqbb'],
handler: (event, context, message) => {
// Handle Timer trigger event
// for a4wt2lnqwvjwnregbqbb timer
}
},
{
triggerId: ['b4wt2lnqwvjwnregbqbb'],
handler: (event, context, message) => {
// Handle Timer trigger event
// for b4wt2lnqwvjwnregbqbb timer
}
},
]
});
`
To handle Message Queue trigger events, add the message_queue key into the routes definition. The only handler param is mandatory for the route.
`typescript
import { router } from 'yandex-cloud-functions-router';
export.handler = router({
message_queue: [
{
queueId: ['a4wt2lnqwvjwnregbqbb'], / Filter by queue identifier (optional). /
body: { }, / Filter by body content (optional). /
validators: { }, / Additional validator(s) for the event. /
handler: (event, context, message) => { / Handler function (required). /
// Handle Message Queue trigger event
}
}
]
});
`
handler accepts three params that came from Yandex Cloud.
It is possible to filter events by queue ID or body content.
To filter events by Queue ID, specify queueId property for a route. It is an optional property.
` export.handler = router({Example
typescript
import { router } from 'yandex-cloud-functions-router';
message_queue: [
{
queueId: ['a4wt2lnqwvjwnregbqbb'],
handler: (event, context, message) => {
// Handle Message Queue trigger event
// for a4wt2lnqwvjwnregbqbb queue
}
},
{
queueId: ['b4wt2lnqwvjwnregbqbb'],
handler: (event, context, message) => {
// Handle Message Queue trigger event
// for b4wt2lnqwvjwnregbqbb queue
}
}
]
});
`
To filter events by Body content, specify body property for a route. You can filter by JSON object properties or regular expression.
To use JSON filtering the request must contain Content-Type: application/json header and body should contain valid JSON object. If these criteria aren't met, the route will be ignored.
It is an optional property.
` export.handler = router({Example (JSON filter)
typescript
import { router } from 'yandex-cloud-functions-router';
message_queue: [
{
body: {
json: {
type: 'add'
}
},
handler: (event, context, message) => {
// Handle Message Queue trigger event
// that has JSON object in body with type=add property.
}
},
{
body: {
json: {
type: 'update'
}
},
handler: (event, context, message) => {
// Handle Message Queue trigger event
// that has JSON object in body with type=update property.
}
}
]
});
`
` export.handler = router({Example (RegExp filter)
typescript
import { router } from 'yandex-cloud-functions-router';
message_queue: [
{
body: {
pattern: /add/i
},
handler: (event, context, message) => {
// Handle Message Queue trigger event
// whose body contains "add" word
}
},
{
body: {
pattern: /update/i
},
handler: (event, context, message) => {
// Handle Message Queue trigger event
// whose body contains "update" word
}
}
]
});
`
Validators supposed to validate the event's content. In contrast to filtering, the route won't be skipped if the validator fails. Instead, exception will be thrown. It is useful to use validators, when you're sure that it's the right route, but want to ensure that incoming request contains the correct data. For instance, if an incoming request doesn't contain the required field, it will fail.
` export.handler = router({ return false;Example
typescript
import { router } from 'yandex-cloud-functions-router';
message_queue: [
{
validators: [
(event, context) => {
if (/ check something in the event /) {
return true;
}
}
],
handler: (event, context, message) => {
// Here you are sure that the request contains the correct data
}
}
]
});
`
To handle Object Storage trigger events, add the object_storage key into the routes definition. The only handler param is mandatory for the route.
`typescript
import { router } from 'yandex-cloud-functions-router';
export.handler = router({
object_storage: [
{
type: ['create'], / Filter by operation type (optional). /
bucketId: ['s3'], / Filter by bucket identifier (optional). /
objectId: ['1.jpg'], / Filter by object identifier (optional). /
handler: (event, context, message) => { / Handler function (required). /
// Handle Object Storage trigger event
}
}
]
});
`
handler accepts three params that came from Yandex Cloud.
It is possible to filter events by type, bucket identifier, and object identifier.
To filter events by type, specify type property for a route. Possible values for this filter are create, update, or delete. It is an optional property.
`Example
typescript`
import { router } from 'yandex-cloud-functions-router';
export.handler = router({
object_storage: [
{
type: ['create'],
handler: (event, context, message) => {
// Handle creating new object in Object Storage
}
},
{
type: ['update'],
handler: (event, context, message) => {
// Handle updating new object in Object Storage
}
},
{
type: ['delete'],
handler: (event, context, message) => {
// Handle deleting new object in Object Storage
}
}
]
});
To filter events by bucket identifier, specify bucketId property for a route. It is an optional property.
`Example
typescript`
import { router } from 'yandex-cloud-functions-router';
export.handler = router({
object_storage: [
{
bucketId: ['s3'],
handler: (event, context, message) => {
// Handle event in "s3" bucket
}
}
]
});
To filter events by object identifier, specify objectId property for a route. It is an optional property.
`Example
typescript`
import { router } from 'yandex-cloud-functions-router';
export.handler = router({
object_storage: [
{
objectId: ['1.jpg'],
handler: (event, context, message) => {
// Handle event in "1.jpg" object
}
}
]
});
To handle IoT Core message trigger events, add the iot_message key into the routes definition. The only handler param is mandatory for the route.
`typescript
import { router } from 'yandex-cloud-functions-router';
export.handler = router({
iot_message: [
{
registryId: ['arenou2oj4ct42eq8g3n'], / Filter by Registry ID (optional). /
deviceId: ['areqjd6un3afc3cefcvm'], / Filter by Device ID (optional). /
mqttTopic: ['$devices/areqjd6un3afc3cefcvm/events'],/ Filter by MQTT Topic (optional). /
handler: (event, context, message) => { / Handler function (required). /
// Handle IoT Core message trigger event
}
}
]
});
`
handler accepts three params that came from Yandex Cloud.
It is possible to filter events by registry identifier, device identifier, and MQTT topic.
To filter events by the registry, specify registryId property for a route. It is an optional property.
`Example
typescript`
import { router } from 'yandex-cloud-functions-router';
export.handler = router({
iot_message: [
{
registryId: ['arenou2oj4ct42eq8g3n'],
handler: (event, context, message) => {
// Handle IoT Core message trigger event
// for arenou2oj4ct42eq8g3n registry
}
}
]
});
To filter events by the device, specify deviceId property for a route. It is an optional property.
`Example
typescript`
import { router } from 'yandex-cloud-functions-router';
export.handler = router({
iot_message: [
{
deviceId: ['areqjd6un3afc3cefcvm'],
handler: (event, context, message) => {
// Handle IoT Core message trigger event
// for areqjd6un3afc3cefcvm device
}
}
]
});
To filter events by MQTT topic, specify mqttTopic property for a route. It is an optional property.
`Example
typescript`
import { router } from 'yandex-cloud-functions-router';
export.handler = router({
iot_message: [
{
mqttTopic: ['$devices/areqjd6un3afc3cefcvm/events'],
handler: (event, context, message) => {
// Handle IoT Core message trigger event
// by $devices/areqjd6un3afc3cefcvm/events topic
}
}
]
});
In case of any exceptions during the request processing, it is possible to map these exceptions to specific output response. To do that specify errorHandling property in router options. There are standard exceptions as well as custom exception handling.
`typescript
import { router } from 'yandex-cloud-functions-router';
export.handler = router({
http: [/ ... /]
},
{
errorHandling: {
http: {
notFound: (error) => ({
statusCode: 404
})
},
unknownEvent: (error) => ({
statusCode: 404
})
}
});
`
The following types of standard errors are supported:
* http / messageQueue / objectStorage / iot:notFound
* — throws when there are no matching route for the current request.invalidRequest
* — throws when the route is found, but there is validation error (during validation process).timer
* :notFound
* — throws when there are no matching route for the current request.unknownEvent
* — throws when the router is unable to determine the type of processed event.unknownMessage
* — throws when the router started to process trigger event, but unable to determine the message type.triggerCombinedError
* — if the function processes few messages at the time, and during the processing few exceptions are thrown, they will be combined into TriggerRouteError and could be handled with triggerCombinedError.
In addition, it is possible to handle errors that came from the handler's logic. To do that add custom section which is an array of definitions for error handling.
`typescript
import { router } from 'yandex-cloud-functions-router';
export.handler = router({
http: [/ ... /]
},
{
errorHandling: {
custom: [
{
error: 'Something happened',
result: (error) => ({
statusCode: 500
})
},
{
error: /error/i,
result: (error) => ({
statusCode: 500
})
}
]
}
});
`
Every definition contains two mandatory properties - error and result.
* error could be either string and RegExp object that match the message of the exception.
* result is a handler similar to normal (non-error) handler.
You can skip to define errorHandling option. In this case default set of error handlers will work, that will return HTTP 404 for notFound, and HTTP 400 for invalidRequest` types. Default error handlers are defined only for HTTP events.
yandex-cloud-functions-router is released under the MIT License.