I am attempting to implement this approach to serving vector tiles from PostGIS using a lambda function and an AWS API Gateway Method: https://www.sparkgeo.com/blog/vector-tile-server-using-postgis. And for the most part, I have it working.

But it doesn't seem to be in the correct format and not a usable vector tile. this is what I am getting back:

enter image description here

And this is what I receive back from https://a.tiles.mapbox.com/v4/mapbox.mapbox-terrain-v2/13/1310/3168.vector.pbf enter image description here

I tried switching the content type to application/x-protobuf, but that had no effect. I think I need to do something additional to the byte array to make it into a consumable vector tile but the blog post was light on details regarding the API Gateway.

But this is what I have setup: enter image description here


I was, after many hours, finally able to work out something that works between the AWS API Gateway and the lambda function.

First, I changed the sparkgeo index.js callback call from:

        var s = b.toString('base64');
        callback(null, {
            statusCode: 200,
            headers: { 'Content-Type': 'application/octet-stream', 'Access-Control-Allow-Origin': '*' },
            body: s,
            isBase64Encoded: true


var s = b.toString('base64');

I found out that if you set Content handling to Convert to binary (CONVERT_TO_BINARY) the entire response has to be base64

Then I had to configure the HTTP Get Method as:

Full Method: enter image description here

Method Request enter image description here

Integration Request enter image description here

Integration Response enter image description here

Method Response enter image description here

And then over in the API Settings, add application/octet-stream to Binary Media Types: enter image description here

And the full method Json definition is:

    "httpMethod": "GET",
    "authorizationType": "NONE",
    "apiKeyRequired": false,
    "requestParameters": {
        "method.request.path.dataset": true,
        "method.request.path.x": true,
        "method.request.path.y": true,
        "method.request.path.z": true
    "methodResponses": {
        "200": {
            "statusCode": "200",
            "responseParameters": {
                "method.response.header.Content-Type": false
            "responseModels": {
                "application/octet-stream": "Empty"
    "methodIntegration": {
        "type": "AWS",
        "httpMethod": "POST",
        "uri": "arn:aws:apigateway:us-east-1:lambda:path/2015-03-31/functions/arn:aws:lambda:us-east-1:795830580453:function:mvt_xxxxxxxxxxx/invocations",
        "requestParameters": {
            "integration.request.path.dataset": "method.request.path.dataset",
            "integration.request.path.x": "method.request.path.x",
            "integration.request.path.y": "method.request.path.y",
            "integration.request.path.z": "method.request.path.z"
        "requestTemplates": {
            "application/json": "##  See http://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-mapping-template-reference.html\n##  This template will pass through all parameters including path, querystring, header, stage variables, and context through to the integration endpoint via the body/payload\n#set($allParams = $input.params())\n{\n\"body-json\" : $input.json('$'),\n\"params\" : {\n#foreach($type in $allParams.keySet())\n    #set($params = $allParams.get($type))\n\"$type\" : {\n    #foreach($paramName in $params.keySet())\n    \"$paramName\" : \"$util.escapeJavaScript($params.get($paramName))\"\n        #if($foreach.hasNext),#end\n    #end\n}\n    #if($foreach.hasNext),#end\n#end\n},\n\"stage-variables\" : {\n#foreach($key in $stageVariables.keySet())\n\"$key\" : \"$util.escapeJavaScript($stageVariables.get($key))\"\n    #if($foreach.hasNext),#end\n#end\n},\n\"context\" : {\n    \"account-id\" : \"$context.identity.accountId\",\n    \"api-id\" : \"$context.apiId\",\n    \"api-key\" : \"$context.identity.apiKey\",\n    \"authorizer-principal-id\" : \"$context.authorizer.principalId\",\n    \"caller\" : \"$context.identity.caller\",\n    \"cognito-authentication-provider\" : \"$context.identity.cognitoAuthenticationProvider\",\n    \"cognito-authentication-type\" : \"$context.identity.cognitoAuthenticationType\",\n    \"cognito-identity-id\" : \"$context.identity.cognitoIdentityId\",\n    \"cognito-identity-pool-id\" : \"$context.identity.cognitoIdentityPoolId\",\n    \"http-method\" : \"$context.httpMethod\",\n    \"stage\" : \"$context.stage\",\n    \"source-ip\" : \"$context.identity.sourceIp\",\n    \"user\" : \"$context.identity.user\",\n    \"user-agent\" : \"$context.identity.userAgent\",\n    \"user-arn\" : \"$context.identity.userArn\",\n    \"request-id\" : \"$context.requestId\",\n    \"resource-id\" : \"$context.resourceId\",\n    \"resource-path\" : \"$context.resourcePath\"\n    }\n}\n"
        "passthroughBehavior": "WHEN_NO_MATCH",
        "contentHandling": "CONVERT_TO_TEXT",
        "timeoutInMillis": 29000,
        "cacheNamespace": "h7bei1",
        "cacheKeyParameters": [],
        "integrationResponses": {
            "200": {
                "statusCode": "200",
                "responseParameters": {
                    "method.response.header.Content-Type": "'application/octet-stream'"
                "contentHandling": "CONVERT_TO_BINARY"
  • I have followed your configuration of API Gateway, but I have problem with the event.pathParameters object - it's undefined now. Don't you know where can be a problem ? Anyway thank you for the detailed documentation of it...I have followed the sparkgeo.com blog, but this is good (and necessary) addition to it. – Michael Knave Nov 18 '20 at 19:46

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.