What is Membrane?

A fast, powerful way to write internal tools in TypeScript, connecting the apps you already use at work.

FEATURES
EXAMPLES
example google sheets hn jobs
Tracks Hacker News Jobs, based on keywords, and saves them to a Google Sheet.
Cronjob invokes watchJobs every hour.
Fetch top jobs from hn.
Append New Google Sheets row
import { nodes, root, state } from "membrane";

export async function configure({ keywords }) {
  state.keywords = keywords;

  root.watchJobs.$cron(`0 0 * * * *`)
}

export async function watchJobs() {
  const query = `{ title url time }`
  const topStories = await nodes.stories.items.$query(query);

  const keywords = state.keywords
    .split(",")
    .map((k) => k.toLowerCase());

  const matchedStories = topStories.filter((story) => {
    if (!story.url) {
      return false;
    }
    const title = story.title?.toLowerCase();
    return keywords.some((k) => title?.includes(k));
  });

  const data = matchedStories.map((story) => {
    const date = new Date(story.time! * 1000)
      .toLocaleDateString("en-US");
    return [story.title, date, story.url];
  });

  await nodes.sheet.append({ values: data });
}
What is Membrane?

A fast, powerful way to write internal tools in TypeScript, connecting the apps you already use at work.

FEATURES
EXAMPLES
Tracks Hacker News Jobs, based on keywords, and saves them to a Google Sheet.
Cronjob invokes watchJobs every hour.
Fetch top jobs from hn.
Append New Google Sheets row
import { nodes, root, state } from "membrane";

export async function configure({ keywords }) {
  state.keywords = keywords;

  root.watchJobs.$cron(`0 0 * * * *`)
}

export async function watchJobs() {
  const query = `{ title url time }`
  const topStories = await nodes.stories.items.$query(query);

  const keywords = state.keywords
    .split(",")
    .map((k) => k.toLowerCase());

  const matchedStories = topStories.filter((story) => {
    if (!story.url) {
      return false;
    }
    const title = story.title?.toLowerCase();
    return keywords.some((k) => title?.includes(k));
  });

  const data = matchedStories.map((story) => {
    const date = new Date(story.time! * 1000)
      .toLocaleDateString("en-US");
    return [story.title, date, story.url];
  });

  await nodes.sheet.append({ values: data });
}
Communication

If you're curious about Membrane's architecture, Juan joined the devtools.fm podcast to talk through the nuts and bolts.



Enter your email for occasional updates