Skip to content

estephinson/funclify

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

29 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Funclify 🤖

Funclify is an opinionated framework for building APIs on Netlify Functions. It's fast, it's powerful and most importantly focused around a great developer experience.

Currently, this is a TypeScript-only framework. This reflects the current focus of the project, being a type-safe and DX focused package, and in the future it may be updated to compile to support ESM. It's early days, so please forgive the narrow-focus.

⚠️ Just one thing!

Funclify is very early on in it's development. It may be abandoned, it may be completely up-ended and rewriten in Rust 👀, so as much as I'd love to say use this in production, bear those things in mind.

Install

# pnpm
pnpm add funclify

# npm
npm install funclify

Basic Use

Funclify includes an API class which is the entry point for both defining handlers and also processing requests.

// netlify/functions/api.ts
import { Api } from 'funclify';

const api = new Api();

api.get("/", async (_, res) => {
    return res.withJSON({ message: "Hello World!" });
});

export const handler = api.baseHandler;

Typed Route Parameters

Funclify is focused on being a strongly-typed framework. This extends to route parameters. Making heavy use of infer, we can create a strongly-typed params property that lives on the req argument passed to your route handler.

api.get("/users/:user_id/orders/:order_id", async({ params }, res) => {
    // params: { user_id: string, order_id: string }
    const { user_id, order_id } = params;

    const order = await fetchOrder(user_id, order_id);

    return res.withJSON(order);
})

Testing

Funclify comes bundled with a test harness to make it simple to run integration tests against your API.

Although you could adopt a more "unit" approach, the framework is built to encourage testing to the boundary of your application for each and every API route.

An example below utilising Vitest

import { describe, it, beforeEach, expect } from "vitest";
import { ApiTestHarness } from "funclify";
import { api } from "../functions/api";

describe("API", () => {
  let test: ApiTestHarness<typeof api>;

  beforeEach(() => {
    // This could be set once rather than in before-each, as
    // in theory an API should be idempotent. However, for flexibility
    // atomicity can be guaranteed by initialising in the beforeEach
    test = new ApiTestHarness(api);
  });

  it("should return a user object", async () => {
    // Perform the request. Under the hood, this
    // emulates the `event` and `context` fed in
    // from a Netlify Function
    const response = await test.get("/user/123");

    expect(response.statusCode).toBe(200);

    // Regardless of your application output, the response
    // from a Netlify Function will be a string, so we need
    // to parse into JSON to assert on the returned objects
    expect(response.body).toBeTypeOf("string");

    const body = JSON.parse(response.body!);
    expect(body).toContain({
      id: "123",
      name: "Ed",
    });
  });
});

About

Netlify Functions Router

Resources

License

Stars

Watchers

Forks

Packages

No packages published