5

GitHub - sebringrose/peko: Featherweight server toolkit for apps on the edge 🐣⚡

 1 year ago
source link: https://github.com/sebringrose/peko
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

peko-logo

  Server     Routing     Request handling     Response caching  

  API DOCS     COMMUNITY TOOLS  

Stateless HTTP(S) apps that are:

  • Featherweight - Browser-native JavaScript + Deno std library + no build-time tooling

  • Functional - Express-like API + full-stack library

  • Production-ready - High test coverage + stable Deno APIs + server profiling utility

  • Community-driven - Popular tool integrations + contributions encouraged

Try the Preact demo

  1. Deno is sick. Install it.

  2. $ git clone https://github.com/sebringrose/peko.git

  3. $ deno task start:dev

Note: Lit-html VS Code plugin recommended.

Deployment

  • Deno Deploy (fork and deploy the examples if you fancy sparkling_heart)
  • Docker (coming soon...)

App showcase

iiisun.art - artistic storefront built with Deno, Peko, React, ImageMagick_deno source

shineponics.org - smart-farming PaaS built with Deno, Peko, React, Google Cloud Platform source

peko-auth.deno.dev - demo app built with Deno, Peko, Preact source

PR to add your project to the showcase raised_hands

What does stateless mean?

Peko apps are designed to boot from scratch at request time and disappear once the request is served. Therefore, storing data in memory between requests (stateful logic) is not reliable. Instead we should use stateless logic and store data within the client or external services.

This paradigm is often referred to as "serverless" or "edge computing" on cloud platforms, which offer code execution on shared server hardware. This is much more resource efficient than traditional server provisioning.

Because our stateless apps cold-start it is important to keep their codebases small. The preact demo app only imports Peko and Preact as external dependencies and is very fast as a result - https://peko.deno.dev!

Note: In reality a single app instance will serve multiple requests, we just can't guarantee it. This is why caching is still an effective optimization strategy but in-memory user sessions are not an effective authentication strategy.

Library overview

Server

The TypeScript server.ts module describes a small framework for building HTTP servers on top of the Deno http/server module.

Here are the main components:

  • Server class: which manages the HTTP server, the routes, and the middleware.
  • RequestContext class: holds information about the server, the request, and state to be shared between middleware.

Main types (types.ts):

  • Route: an object with path, method, middleware, and handler properties.
  • Middleware: a function that receives a RequestContext and updates state or generates a response.
  • Handler: a function that handles requests by receiving a RequestContext and generating a response.

The Server class has several methods for adding and removing routes and middleware, as well as starting the server and handling requests:

  • use(middleware: Middleware | Middleware[] | Router): add global middleware or a router.
  • addRoute(route: Route): adds a route to the server.
  • addRoutes(routes: Route[]): adds multiple routes to the server.
  • removeRoute(route: string): removes a route from the server.
  • removeRoutes(routes: string[]): removes multiple routes from the server.
  • listen(port?: number, onListen?: callback): starts listening to HTTP requests on the specified port.
  • close(): stops to HTTP listener process.
import * as Peko from "https://deno.land/x/peko/mod.ts"; // or "../server.ts" for super featherweight 

const server = new Peko.Server();

server.use(Peko.logger(console.log));

server.addRoute("/hello", () => new Response("Hello world!"));

server.listen(7777, () => console.log("Peko server started - let's go!"));

Routing

Routes can be added to a Server instance directly or a Router instance. Below you can see the different ways routes can be added with addRoute.

import * as Peko from "https://deno.land/x/peko/mod.ts"; // or "https://deno.land/x/peko/server.ts"

const server = new Peko.Server()
server.addRoute("/hello", () => new Response("Hello world!"))
server.removeRoute("/hello");

const router = new Peko.Router()

router.addRoute("/shorthand-route", async (ctx, next) => { await next(); console.log(ctx.request.headers); }, () => new Response("Hello world!"));

router.addRoute({
    path: "/object-route",
    middleware: async (ctx, next) => { await next(); console.log(ctx.request.headers); }, // can also be array of middleware
    handler: () => new Response("Hello world!")
})

router.addRoutes([ /* array of route objects */ ])

server.use(router)

server.listen()

Request handling

Each route must have a handler function that generates a Response. Upon receiving a request the Server will construct a RequestContext and cascade it through any global middleware, then route middleware and finally the route handler. Global and route middleware are invoked in the order they are added. If a response is returned by any middleware along the chain no subsequent middleware/handler will run.

Peko comes with a library of utilities, middleware and handlers for common route use-cases, such as:

  • server-side-rendering
  • opening WebSockets
  • JWT signing/verifying & authentication
  • logging
  • caching

See handlers, mmiddleware or utils for source, or dive into examples for demo implementations.

The second argument to any middleware is the next fcn. This returns a promise that resolves to the first response returned by any subsequent middleware/handler. This is useful for error-handling as well as post-response operations such as logging. See the below snippet or middleware/logger.ts for examples.

If no matching route is found for a request an empty 404 response is sent. If an error occurs in handling a request an empty 500 response is sent. Both of these behaviours can be overwritten with the following middleware:

server.use(async (_, next) => {
    const response = await next();
    if (!response) return new Response("Would you look at that? Nothing's here!", { status: 404 });
});
server.use(async (_, next) => {
    try {
        await next();
    } catch(e) {
        console.log(e);
        return new Response("Oh no! An error occured :(", { status: 500 });
    }
});

Response caching

In stateless computing, memory should only be used for source code and disposable cache data. Response caching ensures that we only store data that can be regenerated or refetched. Peko provides a ResponseCache utility for this with configurable item lifetime. The cacher middleware wraps it and provides drop in handler memoization and response caching for your routes.

const cache = new Peko.ResponseCache({ lifetime: 5000 });

server.addRoute("/do-stuff", Peko.cacher(cache), () => new Response(Date.now()));

And that's it! Check out the API docs for deeper info. Otherwise happy coding nerd_face

Motivations

The modern JS edge is great because the client-server gap practically disappears. We can have all of the SEO and UX benefits of SSR without any JavaScript transpilation or bundling. We can use modules and classes in the browser until users decide they want cloud compute. If we want TS source we can emit JS versions of code. This completely eliminates part of the traditional JavaScript toolchain, increasing project maintainability and simplicity, all while making our software even faster.

Better yet, Peko is not build for any specific frontend framework or library. You can use barebones HTML, React, Preact, Vue... you name it. Simply plug your app-rendering logic into the Render function of an ssrHandler.

This is all made possible by modern JavaScript runtimes. Deno is built to the ECMAScript specification. This makes it compatible with browser JavaScript which elimates the need to generate separate client and server JavaScript bundles (the support for URL imports is the secret sauce). UI libraries like Preact combined with htm offer lightning fast client-side hydration with a browser-friendly markup syntax. On top of this Deno has native TypeScript support, a rich runtime API and loads of community tools for your back-end needs.

This project started out of excitement for the elegancy of Deno and the freedom it would bring to the JavaScript community. If you are interested in contributing please submit a PR or get in contact ^^


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK