Skip to content

tclain-pillar/hattip

 
 

Repository files navigation

HatTip

(nothing) Like Express.js.

Follow: Twitter > @cyco130 & Twitter > @brillout
Chat: Discord > CubeshashHatTip

Why HatTip?

Instead of writing server code that only works with Express.js, write server code that can be deployed anywhere: AWS, Cloudflare Workers, Vercel, VPS, ...

What is HatTip?

HatTip is a set of JavaScript packages for building HTTP server applications.

  • ✨ Modern: Based on current and future web standards (Fetch API & WinterCG).
  • 🌍 Universal: Runs anywhere (Node.js, the Edge, Deno, ...).
  • 🧩 Modular: Use as much or as little as you need.
  • 🪛 Minimalist: Everything you need, nothing you don't.

It aims to build an ecosystem of universal middlewares that can be used across the entire JavaScript universe.

// handler.js

// This request handler works anywhere, e.g. Node.js or Cloudflare Workers.

export default (context) => {
  const { pathname } = new URL(context.request.url);
  if (pathname === "/") {
    return new Response("Hello from HatTip.");
  } else if (pathname === "/about") {
    return new Response(
      "This HTTP handler works in Node.js and Cloudflare Workers.",
    );
  } else {
    return new Response("Not found.", { status: 404 });
  }
};

A HatTip handler is passed a context object which represents the request context and contains context.request which is a standard Request object. It returns a standard Response object (or a promise of one). Response and Request follow the Fetch API standard. So if you're familiar with service workers, Cloudflare Workers, or Deno, you'll feel right at home. If not, learn today the standard that will tomorrow be ubiquitous.

We believe in a diverse but interoperable future for the JavaScript ecosystem and we're closely following the WinterCG which lays the foundation beyond the Fetch API.

Adapters

  • ✅ Node.js
  • ✅ Cloudflare Workers
  • ✅ Express.js (use HatTip handlers/middlewares in your Express.js app)
  • ✅ Vercel Serverless
  • ✅ Vercel Edge
  • ✅ Netlify Functions
  • ✅ Netlify Edge Functions
  • ✅ Deno (including Deno Deploy)
  • ✅ Bun
  • ✅ Lagon
  • ✅ uWebSockets.js
  • 🚧 Service Workers

Adapters let you run HatTip on any platform. Here's how you can use HatTip with Node.js:

// entry-node.js
import { createServer } from "@hattip/adapter-node";
import handler from "./handler.js";

createServer(handler).listen(3000, "localhost", () => {
  console.log("Server listening on http://localhost:3000");
});

...and on Cloudflare Workers:

// entry-cfw.js
import cloudflareWorkersAdapter from "@hattip/adapter-cloudflare-workers";
import handler from "./handler.js";

export default {
  fetch: cloudflareWorkersAdapter(handler),
};

You can even use your HatTip application as an Express middleware when you have to use that one Express library that doesn't have a replacement anywhere else:

// entry-express.js
import { createMiddleware } from "@hattip/adapter-node";
import handler from "./handler.js";
import express from "express";
import oldAndRustyExpressMiddleware from "old-and-rusty-express-middleware";

const hattip = createMiddleware(handler);
const app = express();

// TODO: Replace with shinyNewHatTipMiddleware once ready
app.use(oldAndRustyExpressMiddleware());
app.use(hattip);

app.listen(3000, "localhost", () => {
  console.log("Server listening on http://localhost:3000");
});

Middleware system

The compose function from the @hattip/compose package can be used to compose multiple handlers into a single one, creating a simple but powerful middleware system. Each handler is called in sequence until one returns a response. A handler can pass control to the next handler either by not returning anything or calling ctx.next(). The latter allows the handler to modify the response before returning:

import { compose } from "@hattip/compose";

// Example of making things available in `ctx`
// Middleware to parse the URL into a URL object
const urlParser = (ctx) => {
  ctx.url = new URL(ctx.request.url);
};

// Example of modifying the response
// Middleware to add an X-Powered-By header
const poweredBy = async (ctx) => {
  const response = await ctx.next();
  response.headers.set("X-Powered-By", "HatTip");
  return response;
};

// HatTip does have a router, this is to illustrate the basics
const homeHandler = (ctx) => {
  if (ctx.url.pathname === "/") {
    return new Response("Home");
  }
};

const fooHandler = (ctx) => {
  if (ctx.url.pathname === "/foo") {
    return new Response("Foo");
  }
};

const barHandler = (ctx) => {
  if (ctx.url.pathname === "/bar") {
    return new Response("Bar");
  }
};

export default compose(
  urlParser,
  poweredBy,
  homeHandler,
  fooHandler,
  barHandler,
);

A handler can return or throw a Response or anything with a toResponse method when used with the compose function. Handlers can also set context.handleError to handle uncaught errors.

That's it. This is the entirety of the HatTip API. Everything else is middleware functions similar the above that add various features and development tools to make your life easier.

Packages

HatTip is extremely modular so you can use as little or as much as you need:

  • core: A type-only package that defines the interface between your application and platform adapters
  • Adapters: Enable HatTip to run on any platform:
  • Bundlers: Worker and serverless platforms usually require your code to be in bundled form. These packages provide bundlers fine-tuned for their respective platforms:
  • Low-level stuff
    • polyfills: A collection of polyfills used by adapters for compatibility across platforms
    • compose: A middleware system for combining multiple handlers into a single one
  • Utilities and middleware
    • router: Express-style imperative router
    • response: Utility functions for creating text, JSON, HTML, and server-sent event responses
    • headers: Header value parsing and content negotiation utilities
    • multipart: Experimental multipart parser (e.g. for form data with file uploads)
    • cookie: Cookie handling middleware
    • cors: CORS middleware
    • graphql: GraphQL middleware
    • session: Session middleware

A zero-config development environment based on Vite is also in the works.

Credits

MIT license

About

Like Express, but for the future

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • TypeScript 89.8%
  • JavaScript 9.9%
  • Other 0.3%