VRAMEWORK.IO

FUNCTIONS AS A DESIGN


// Creating this file and running generateRoutes will automatically add this API endpoint to
// express or/and serverless.
import { APIFunction, APIPermission, APIRoutes } from "../api"
import { UserNotFoundError } from "../errors"
import * as DB from '../../generated/db-types'

/**
 * The IN data type to validate against
 */
export interface SendGreetingCard {
  toUserId: string,
  emailText: string
}

/**
 * The resulting data type
 */
export interface SendGreetingCardResult {
  message: string
}

/**
 * The API function. This will be invoked with the services, incoming data
 * and session
 */
const sendGreetingCard: APIFunction<SendGreetingCard, SendGreetingCardResult> =
  async (services, { toUserId, emailText }, { userId }) => {
    // This line can be any database driver
    const [fromUser, toUser] = await Promise.all([
      services.database.crudGet<DB.User>('user', ['email'], { userId }, new UserNotFoundError()),
      services.database.crudGet<DB.User>('user', ['email'], { userId: toUserId }, new UserNotFoundError())
    ])

    // Assuming you have en email service hooked up!
    await services.email.sendEmail({
      template: 'getting',
      from: fromUser.email,
      to: toUser.email,
      body: emailText
    })

    return {
      message: 'Email sent!'
    }
  }

/*
* A Permission function, returns true/false to allow/dissallow access to the API Function
*/
const isBelowEmailLimit: APIPermission<SendGreetingCard> = async (services, data, session) => {
  const { emailsSent } = await services.database.crudGet<DB.User>('user', ['emailsSent'], { userId: session.userId }, new UserNotFoundError())
  return emailsSent <= 100
}

/*
* A Permission function, returns true/false to allow/dissallow access to the API Function
*/
const isPaidMember: APIPermission<SendGreetingCard> = async (services, data, session) => {
  return session.isPaidMember
}

export const routes: APIRoutes = [{
  // The TYPE of HTTP Message
  type: 'post',
  // The HTTP Route (supports query and path params)
  route: 'v1/send-greeting-card',
  // The function to execute
  func: sendGreetingCard,
  // The JSON schema to generate from typescript and validate against
  schema: 'SendGreetingCard',
  // A set of permissions to check against, at least one has to be valid
  permissions: {
    // Either a collection of permissions to be anded
    canSendCard: [isBelowEmailLimit],
    // Or a single one
    isPaidMember
  }
}]
        

Design

  • Typescript ❤️

    Everything is typed, starting from your database itself

    Read more
  • HTTP Agnostic

    Write your code without any HTTP concepts, just objects and errors

    Read more
  • Framework Agnostic

    Concentrate on writing code. Ignore the overhead of specifics with AWS, Azure, via express.

    Read more
  • Autogenerated Schemas and Route discovery

    Run helper functions to avoid having to wire up routes yourself

    Read more

Code

  • Dependency Injection

    Inject your dependencies into each function, making each function truly encapsulated

    Read more
  • Permissions

    Check each function invocation against group permissions before they even run

    Read more
  • User Sessions

    Provide the user session to each function invocation for context

    Read more
  • JSON Schemas

    Generate json schemas automatically from Typescript and validate every API call against them

    Read more

Run

  • Via Express

    Run your functions via express, for local development or deployments via docker / baremetal

    Read more
  • Via Serverless

    Run your functions via serverless, saving you money and hassle

    Read more
  • Via Binary

    Run your functions via a binary, putting all your dependencies into one small package

    Read more