Reference docs


HTTP functions

API reference (legacy / v1 APIs)

Overview

This is the legacy HTTP function reference document pertaining to API Gateway v1 (aka API Gateway "REST" APIs) request/response payloads.

As of September 2020, Begin apps use API Gateway v2 (aka "HTTP" APIs) payloads; this document will be kept in posterity for reference, but is not actively maintained.

For the most up to date documentation on API Gateway v1 REST APIs, please refer to this AWS doc.

HTTP handler API

The HTTP handler API follows a simple request / response pattern. Let's look at an example of a basic HTTP function:

// src/http/get-index/index.js
let body = `
<!doctype html>
<html lang=en>
  <body>
    <h1>Hello world!</h1>
  </body>
</html>
`

exports.handler = async function http (request) {
  return {
    statusCode: 200,
    headers: { 'content-type': 'text/html; charset=utf8' },
    body
  }
}

No sweat, right?

Requests

The handler function invoked by a client request receives a request object containing the following parameters:

  • httpMethod - String
    • One of GET, POST, PATCH, PUT, or DELETE
  • path - String
    • The absolute path of the request
  • pathParameters - null or Object
    • Any URL params, if defined in your HTTP Function's path (e.g. foo in GET /:foo/bar)
  • queryStringParameters - null or Object
    • Any query params if present in the client request
  • headers - Object
    • All client request headers
  • body - null or String (base64-encoded)
    • Contains unparsed, base64-encoded request body
    • We suggest parsing with our body parser helper
  • isBase64Encoded - Boolean
    • Indicates whether body is base64-encoded binary payload

Additional and extended versions of this and other data may be available in your request; for additional documentation, please head here.

Example

Here's an example of an incoming request object, being handled by the HTTP Function GET /salutations/:greeting:

// Client requested https://your-domain/salutations/hello-world?testing=123
{
  httpMethod: 'GET',
  path: '/salutations/hello-world',
  headers: {
    host: 'begin.com',
    connection: 'keep-alive',
    'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36',
    accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
    dnt: '1',
    'accept-encoding': 'gzip, deflate, br',
    'accept-language': 'en-US,en;q=0.9',
    Cookie: '_idx=LbyL0kPK2xOLfdm_WnESzlsG.8kStzevVXstnEkosp0k5PK2xOz3e820NtoEx1b3VXnEC8'
  },
  queryStringParameters: { testing: '123' },
  pathParameters: { greeting: 'hello-world' },
  body: null,
  isBase64Encoded: false
}

Parsing bodies

All bodies are unparsed, base64-encoded strings; you can parse and process these however you please, but @architect/functions has a convenient method for doing so. Here's an example:

// POST body includes form URL-encoded string: 'greeting=howdy'
let arc = require('@architect/functions')
let parseBody = arc.http.helpers.bodyParser

exports.handler = async function http (request) {
  console.log(request.body)     // 'Z3JlZXRpbmc9aG93ZHk='
  let body = parseBody(request) // Pass the entire request object
  let greeting = body.greeting  // 'howdy'
  return {
    statusCode: 200,
    headers: { 'content-type': 'text/html; charset=utf8' },
    body: greeting
  }
}

Responses

Responses are returned by your handler function in an object, and use the following parameters:

  • statusCode - Number
    • Sets the HTTP status code; defaults to 200
  • headers - Object
    • All response headers
  • body - String
    • Contains response body, either as a plain string (no encoding or serialization required) or, if binary, a base64-encoded buffer
    • Note: The maximum body payload size is 6MB; files being delivered non-dynamically should use the Begin CDN
  • isBase64Encoded - Boolean
    • Indicates whether body is base64-encoded binary payload; defaults to false
    • Required to be set to true if emitting a binary payload

Example

Here's a simple example response for an API endpoint:

// Responding to a successful POST
return {
  statusCode: 201,
  headers: { 'content-type': 'application/json; charset=utf8' },
  body: JSON.stringify({ok: true}),
}

Anti-caching headers

Many remote networks rely on overly aggressive reverse-proxy caches to conserve bandwidth; the absence of the cache-control header is often (mis)construed by such networks as tacit permission to aggressively cache responses that often should not be cached. This external, network-level behavior can have serious ramifications for your app.

Because of the highly adverse effects network-level caching can on your application, we strongly suggest that most HTTP Function responses include anti-caching headers – especially when returning HTML and JSON responses. For example:

return {
  statusCode: 200,
  headers: {
    'content-type': 'text/html; charset=utf8',
    'cache-control': 'no-cache, no-store, must-revalidate, max-age=0, s-maxage=0'
  },
  body: `This definitely won't be cached.`
}

One of the many benefits of using Begin's runtime library, Architect Functions, is automatic, customizable, content-type aware generation of cache-control headers.

Persisting data

Every Begin app comes bundled with Begin Data, an easy to use, fast, durable, highly scalable, and fully managed key-value and document database.

Learn more about integrating Begin Data in your app's Functions!

Examples

Basic web response using sessions

let begin = require('@architect/functions')

exports.handler = async function http (request) {
  let session = await begin.http.session.read(request)
  let name = session.name || 'there'
  let body = `
<!doctype html>
<html lang=en>
  <body>
    <h1>Hello ${name}!</h1>
  </body>
</html>
`
  return {
    statusCode: 200,
    headers: { 'content-type': 'text/html; charset=utf8' },
    body
  }
}

Forward a request

exports.handler = async function http (request) {
  return {
    statusCode: 302,
    headers: { location: '/new/path' }
  }
}

Write a request to Begin Data

let data = require('@begin/data')

exports.handler = async function http (request) {
  let headers = { 'content-type': 'application/json; charset=utf8' }
  if (!request.body.note) {
    return {
      statusCode: 204,
      headers,
      body: JSON.stringify({ ok: false })
    }
  }
  else {
    let table = 'notes'
    let note = request.body.note
    await data.set({table, note})
    return {
      statusCode: 201,
      headers,
      body: JSON.stringify({ ok: true })
    }
  }
}