Serverless architecture allows you to write and deploy applications without managing the underlying servers. It means software developers can focus on writing code without worrying about the infrastructure required to execute it. That can translate to greater scalability, flexibility, and faster time to value.
You can achieve even more when you combine serverless compute abilities with Tyk’s API management. Here’s how.
Tyk and AWS Lambda – the perfect pairing
Many vendors offer serverless compute abilities. Today, we’re focusing on AWS Lambda. Lambda is a compute service that allows developers to build, debug and run applications. We previously discussed how to leverage Lambda function URLs. Now, it’s time to look at how you can invoke AWS Lambda functions directly from Tyk’s powerful, extensible API gateway!
Figure 1: An image depicting the invocation of an AWS Lambda function directly from Tyk-Gateway.
Methodology and code
You’ll need between 10 and 20 minutes to do this, along with the following system prerequisites:
- Docker
- GO Version: go1.20.10 darwin/amd64
- git
- IAM credentials with Lambda access
- A Lambda function provisioned named “custom_lambda_function”
Follow these steps to begin:
Clone the custom-go-plugin repository with the following:
``` git clone https://github.com/TykTechnologies/custom-go-plugin.git ```
This repository can be used with open source Tyk or licensed Tyk; for this blog, we will assume you have a licensed version of Tyk. In order to obtain a license key, please visit the following link here.
The contents of reference 1 go in custom-go-plugin/tyk/bundle/manifest.json
The contents of reference 2 go in custom-go-plugin/go/src/CustomGoPlugin.go
Bring up the stack with the following command:
``` make ```
The contents of reference 3 are imported into the Tyk-Dashboard as an API.
Finally, reference 4 is an example of how to invoke the API which invokes an AWS Lambda behind the scenes.
Click to expand – Reference 1: A manifest.json file illustrating which plugin is being executed, where it’s executed and when it’s executed.
{ "file_list": [ "CustomGoPlugin_v5.0.1_linux_amd64.so" ], "custom_middleware": { "post": [ { "name": "InvokeLambda", "path": "CustomGoPlugin.so", "require_session": false, "raw_body_only": false } ], "driver": "goplugin", "id_extractor": { "extract_from": "", "extract_with": "", "extractor_config": {} } }, "checksum": "", "signature": "" }
Click to expand – Reference 2: A CustomGoPlugin.go file specifying the required Go dependencies as well as the function InvokeLambda that will be triggered by the Tyk-Gateway.
package main import ( "context" "encoding/json" "fmt" "github.com/TykTechnologies/tyk/log" "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/credentials" "github.com/aws/aws-sdk-go-v2/service/lambda" "github.com/aws/aws-sdk-go-v2/service/lambda/types" "net/http" ) var logger = log.Get() func InvokeLambda(rw http.ResponseWriter, r *http.Request) { // Read AWS Configuration cfg, err := config.LoadDefaultConfig(context.TODO(), config.WithRegion("us-east-1"), config.WithCredentialsProvider(credentials.NewStaticCredentialsProvider( "AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY", "AWS_SESSION_TOKEN")), ) if err != nil { fmt.Println("Couldn't load default configuration. Have you set up your AWS account?") fmt.Println(err) return } function := r.Header.Get("function") if len(function) == 0 { function = "custom_lambda_function" } logger.Info("Function: '%v'", function) lambdaClient := lambda.NewFromConfig(cfg) payload, err := json.Marshal("") invokeOutput, err := lambdaClient.Invoke(context.TODO(), &lambda.InvokeInput{ FunctionName: aws.String(function), LogType: types.LogTypeNone, Payload: payload, }) rw.WriteHeader(http.StatusOK) write, err := rw.Write(invokeOutput.Payload[:]) if err != nil { return } if write > 0 { } } func main() {} func init() { logger.Info("--- Go custom plugin v5 init success! ---- ") }
Click to expand – Reference 3: An API definition that you can import through the Dashboard in order to leverage the Lambda
{ "created_at": "2023-12-06T19:59:03Z", "api_model": {}, "api_definition": { "api_id": "a1ccb5e98f4e48355cc6aa078a6f797d", "jwt_issued_at_validation_skew": 0, "upstream_certificates": {}, "use_keyless": true, "enable_coprocess_auth": false, "base_identity_provided_by": "", "custom_middleware": { "pre": [], "post": [ { "disabled": false, "name": "InvokeLambda", "path": "CustomGoPlugin.so", "require_session": false, "raw_body_only": false } ], "post_key_auth": [], "auth_check": { "disabled": false, "name": "", "path": "", "require_session": false, "raw_body_only": false }, "response": [], "driver": "goplugin", "id_extractor": { "disabled": false, "extract_from": "", "extract_with": "", "extractor_config": {} } }, "disable_quota": false, "custom_middleware_bundle": "", "cache_options": { "cache_timeout": 60, "enable_cache": true, "cache_all_safe_requests": false, "cache_response_codes": [], "enable_upstream_cache_control": false, "cache_control_ttl_header": "", "cache_by_headers": [] }, "enable_ip_blacklisting": false, "tag_headers": [], "jwt_scope_to_policy_mapping": {}, "pinned_public_keys": {}, "expire_analytics_after": 0, "external_oauth": { "enabled": false, "providers": [] }, "domain": "", "openid_options": { "providers": [], "segregate_by_client": false }, "jwt_policy_field_name": "", "enable_proxy_protocol": false, "jwt_default_policies": [], "active": true, "jwt_expires_at_validation_skew": 0, "config_data": {}, "notifications": { "shared_secret": "", "oauth_on_keychange_url": "" }, "jwt_client_base_field": "", "auth": { "disable_header": false, "auth_header_name": "Authorization", "cookie_name": "", "name": "", "validate_signature": false, "use_param": false, "signature": { "algorithm": "", "header": "", "use_param": false, "param_name": "", "secret": "", "allowed_clock_skew": 0, "error_code": 0, "error_message": "" }, "use_cookie": false, "param_name": "", "use_certificate": false }, "check_host_against_uptime_tests": false, "auth_provider": { "name": "", "storage_engine": "", "meta": {} }, "custom_plugin_auth_enabled": false, "blacklisted_ips": [], "graphql": { "schema": "", "enabled": false, "engine": { "field_configs": [], "data_sources": [], "global_headers": [] }, "type_field_configurations": [], "execution_mode": "proxyOnly", "proxy": { "auth_headers": {}, "request_headers": {}, "use_response_extensions": { "on_error_forwarding": false } }, "subgraph": { "sdl": "" }, "supergraph": { "subgraphs": [], "merged_sdl": "", "global_headers": {}, "disable_query_batching": false }, "version": "2", "playground": { "enabled": false, "path": "" } }, "hmac_allowed_clock_skew": -1, "dont_set_quota_on_create": false, "uptime_tests": { "check_list": [], "config": { "expire_utime_after": 0, "service_discovery": { "use_discovery_service": false, "query_endpoint": "", "use_nested_query": false, "parent_data_path": "", "data_path": "", "cache_timeout": 60 }, "recheck_wait": 0 } }, "enable_jwt": false, "do_not_track": false, "name": "lambda", "slug": "lambda", "analytics_plugin": {}, "oauth_meta": { "allowed_access_types": [], "allowed_authorize_types": [], "auth_login_redirect": "" }, "CORS": { "enable": false, "max_age": 24, "allow_credentials": false, "exposed_headers": [], "allowed_headers": [ "Origin", "Accept", "Content-Type", "X-Requested-With", "Authorization" ], "options_passthrough": false, "debug": false, "allowed_origins": [ "*" ], "allowed_methods": [ "GET", "POST", "HEAD" ] }, "event_handlers": { "events": {} }, "proxy": { "target_url": "http://httpbin.org/", "service_discovery": { "endpoint_returns_list": false, "cache_timeout": 0, "parent_data_path": "", "query_endpoint": "", "use_discovery_service": false, "_sd_show_port_path": false, "target_path": "", "use_target_list": false, "use_nested_query": false, "cache_disabled": false, "data_path": "", "port_data_path": "" }, "check_host_against_uptime_tests": false, "transport": { "ssl_insecure_skip_verify": false, "ssl_ciphers": [], "ssl_min_version": 0, "ssl_max_version": 0, "ssl_force_common_name_check": false, "proxy_url": "" }, "target_list": [], "preserve_host_header": false, "strip_listen_path": true, "enable_load_balancing": false, "listen_path": "/lambda/", "disable_strip_slash": true }, "client_certificates": [], "use_basic_auth": false, "version_data": { "not_versioned": true, "default_version": "", "versions": { "Default": { "name": "Default", "expires": "", "paths": { "ignored": [], "white_list": [], "black_list": [] }, "use_extended_paths": true, "extended_paths": { "ignored": [], "white_list": [], "black_list": [], "transform": [], "transform_response": [], "transform_jq": [], "transform_jq_response": [], "transform_headers": [], "transform_response_headers": [], "hard_timeouts": [], "circuit_breakers": [], "url_rewrites": [], "virtual": [], "size_limits": [], "method_transforms": [], "track_endpoints": [], "do_not_track_endpoints": [], "validate_json": [], "internal": [], "persist_graphql": [] }, "global_headers": {}, "global_headers_remove": [], "global_response_headers": {}, "global_response_headers_remove": [], "ignore_endpoint_case": false, "global_size_limit": 0, "override_target": "" } } }, "jwt_scope_claim_name": "", "use_standard_auth": false, "session_lifetime": 0, "hmac_allowed_algorithms": [], "detailed_tracing": false, "disable_rate_limit": false, "definition": { "enabled": false, "name": "", "default": "", "location": "header", "key": "x-api-version", "strip_path": false, "strip_versioning_data": false, "versions": {} }, "use_oauth2": false, "jwt_source": "", "jwt_signing_method": "", "config_data_disabled": false, "jwt_not_before_validation_skew": 0, "use_go_plugin_auth": false, "jwt_identity_base_field": "", "allowed_ips": [], "request_signing": { "is_enabled": false, "secret": "", "key_id": "", "algorithm": "", "header_list": [], "certificate_id": "", "signature_header": "" }, "org_id": "5e9d9544a1dcd60001d0ed20", "enable_ip_whitelisting": false, "global_rate_limit": { "rate": 0, "per": 0 }, "protocol": "", "enable_context_vars": false, "tags": [], "basic_auth": { "disable_caching": false, "cache_ttl": 0, "extract_from_body": false, "body_user_regexp": "", "body_password_regexp": "" }, "listen_port": 0, "session_provider": { "name": "", "storage_engine": "", "meta": {} }, "auth_configs": { "authToken": { "disable_header": false, "auth_header_name": "Authorization", "cookie_name": "", "name": "", "validate_signature": false, "use_param": false, "signature": { "algorithm": "", "header": "", "use_param": false, "param_name": "", "secret": "", "allowed_clock_skew": 0, "error_code": 0, "error_message": "" }, "use_cookie": false, "param_name": "", "use_certificate": false }, "basic": { "disable_header": false, "auth_header_name": "Authorization", "cookie_name": "", "name": "", "validate_signature": false, "use_param": false, "signature": { "algorithm": "", "header": "", "use_param": false, "param_name": "", "secret": "", "allowed_clock_skew": 0, "error_code": 0, "error_message": "" }, "use_cookie": false, "param_name": "", "use_certificate": false }, "coprocess": { "disable_header": false, "auth_header_name": "Authorization", "cookie_name": "", "name": "", "validate_signature": false, "use_param": false, "signature": { "algorithm": "", "header": "", "use_param": false, "param_name": "", "secret": "", "allowed_clock_skew": 0, "error_code": 0, "error_message": "" }, "use_cookie": false, "param_name": "", "use_certificate": false }, "hmac": { "disable_header": false, "auth_header_name": "Authorization", "cookie_name": "", "name": "", "validate_signature": false, "use_param": false, "signature": { "algorithm": "", "header": "", "use_param": false, "param_name": "", "secret": "", "allowed_clock_skew": 0, "error_code": 0, "error_message": "" }, "use_cookie": false, "param_name": "", "use_certificate": false }, "jwt": { "disable_header": false, "auth_header_name": "Authorization", "cookie_name": "", "name": "", "validate_signature": false, "use_param": false, "signature": { "algorithm": "", "header": "", "use_param": false, "param_name": "", "secret": "", "allowed_clock_skew": 0, "error_code": 0, "error_message": "" }, "use_cookie": false, "param_name": "", "use_certificate": false }, "oauth": { "disable_header": false, "auth_header_name": "Authorization", "cookie_name": "", "name": "", "validate_signature": false, "use_param": false, "signature": { "algorithm": "", "header": "", "use_param": false, "param_name": "", "secret": "", "allowed_clock_skew": 0, "error_code": 0, "error_message": "" }, "use_cookie": false, "param_name": "", "use_certificate": false }, "oidc": { "disable_header": false, "auth_header_name": "Authorization", "cookie_name": "", "name": "", "validate_signature": false, "use_param": false, "signature": { "algorithm": "", "header": "", "use_param": false, "param_name": "", "secret": "", "allowed_clock_skew": 0, "error_code": 0, "error_message": "" }, "use_cookie": false, "param_name": "", "use_certificate": false } }, "custom_middleware_bundle_disabled": false, "strip_auth_data": false, "id": "6570d2870625a500013a2886", "certificates": [], "enable_signature_checking": false, "use_openid": false, "internal": false, "jwt_skip_kid": false, "enable_batch_request_support": false, "enable_detailed_recording": false, "scopes": { "jwt": {}, "oidc": {} }, "response_processors": [], "use_mutual_tls_auth": false }, "hook_references": [], "is_site": false, "sort_by": 0, "user_group_owners": [], "user_owners": [] }
Click to expand – Reference 4: A sample curl call illustrating how you can invoke the API loaded from Reference 3, and specifying a function to invoke from the header.
curl --location 'http://localhost:8081/lambda/' \ --header 'function: custom_lambda_function
Putting the power of serverless architecture in your hands
Server-based architectures rely on always-on servers to handle application processing. Serverless, by contrast, can offer you superior scalability and flexibility – plus the chance of faster time to value from end-to-end development. If that sounds like the approach you need, why not use Tyk with AWS Lambda to leverage the true power of serverless architecture?
You can chat to our team for further details or dive straight in and get started.