npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

apiplant

v0.1.0

Published

The apiplant module TypeScript functions import: typed Postgres, cache, email and function declarations

Readme

apiplant — the module TypeScript functions import

The one module an apiplant function can import. It is what a .ts file in an app's functions/ directory reaches for:

import { defineFunctions, db, s, log } from "apiplant";

const NewNote = s.object({
  title: s.string({ minLength: 1 }),
  tags: s.optional(s.array(s.string())),
});

export default defineFunctions({
  createNote: {
    permission: "authenticated",
    description: "Files a note.",
    input: NewNote,
    handler(input) {
      log.info(`filing ${input.title}`);
      const row = db.one(sql`INSERT INTO apiplant_note (title) VALUES (${input.title}) RETURNING id`);
      return { id: row.id };
    },
  },
});

You do not install this. The module is compiled into the apiplant binary and served to the V8 isolate that runs your function, and apiplant build copies apiplant.d.ts and a tsconfig.json into your functions/ directory so an editor types all of it. There is no node_modules, no package.json in your app, and nothing to keep in step with the server version.

The package here exists so the module has one home: the runtime the isolate executes, the declarations an editor reads, and the tests that check they agree.

typescript/
├── apiplant.js     the runtime, embedded into `apiplant-js` at compile time
├── apiplant.d.ts   the types, copied into functions/ by `apiplant build`
└── test/types.ts   what `pnpm check` compiles: the promises, exercised

What it removes

Without it a function declares a manifest array, exports a matching function per entry, casts every query result, and opens each handler by checking the body by hand. With it:

| | | |---|---| | defineFunctions | a name is written once, beside the handler it names | | s.object({...}) | one declaration becomes the JSON Schema in the docs, the 400 for a bad body, and the type of input | | db.query / one / first / value / execute | typed rows, and the SELECT/rows_affected split handled | | sql`…${value}` | placeholders numbered and values bound, never interpolated | | cache | get/set/has/increment/ttl/remember instead of {op, key} objects | | email.send | the host's Message, typed | | config, principalId, hook, log | the rest of the host, as functions rather than as a threaded argument | | BadRequest, HttpError | the 400/500 split, as exceptions |

Everything is synchronous: the isolate blocks while the host does the work on another thread. async appears in a handler only when it wants it.

Working on it

pnpm install
pnpm check        # tsc over the declarations and their test

test/types.ts is a compile-time test: it asserts that a handler's input is inferred from its schema (and is not silently any), that required fields are required, and that each host call takes what the Rust side actually parses. It runs nothing; tsc failing is the failure.

The Rust side has the other half of the tests — crates/apiplant-js/tests/ loads this module into a real isolate against a stub host and asserts on the wire format between them.

Publishing

The package is publishable but not published: an app never installs it, so npm would only serve editors that would rather resolve apiplant from node_modules than from the generated apiplant.d.ts. If that changes, files and exports are already set for it.

See docs/functions.md for the whole story, and examples/17-typescript-functions for a working app.