Giter Site home page Giter Site logo

sejori / peko Goto Github PK

View Code? Open in Web Editor NEW
154.0 3.0 9.0 5.94 MB

Featherweight apps on the edge, built with Web Standards ๐Ÿฃโšก

Home Page: https://peko.deno.dev/

License: GNU General Public License v3.0

TypeScript 100.00%
http typescript middleware bunjs cloudflare-workers denojs router

peko's Introduction

peko-chick

Peko

Fast - Regex route matcher. Works out-of-the-box with Deno, Bun & Cloudflare Workers ๐ŸŽ๏ธ๐Ÿ’จ

Featherweight - Functional API built with Web Standards & zero dependencies ๐ŸŒ๐Ÿชถ

Feature-rich - Static files, auth, server-sent events & server profiling utils ๐Ÿคน๐Ÿ”

ย  Types ย  ย  Routing ย  ย  Request handling ย  ย  Response caching ย 

Overview

Routes and middleware are added to a Router instance with .use, .addRoute or .get/post/put/delete.

The router is then used with your web server of choice, e.g. Deno.serve or Bun.serve.

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

const router = new Peko.Router();

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

router.get("/shorthand-route", () => new Response("Hello world!"));

router.post("/shorthand-route-ext", async (ctx, next) => { await next(); console.log(ctx.request.headers); }, (req) => new Response(req.body));

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 */ ])

Deno.serve((req) => router.handle(req))

Types

The main class of Peko, provides handle method to generate Response from Request via configured routes and middleware.

Objects with path, method, middleware, and handler properties. Requests are matched to a regex generated from the given path. Dynamic parameters are supported in the /users/:userid syntax.

An object containing url, params and state properties that is provided to all middleware and handler functions associated to a router or matched route.

Functions that receives a RequestContext and a next fcn. Should update ctx.state, perform side-effects or return a response.

The final request handling function on a Route. Must generate and return a response using the provided request context.

Request handling

Each route must have a handler function that returns/resolves to a Response and can optionally have middleware too.

ctx: RequestContext

Upon receiving a request a Router will construct a RequestContext and cascade it through global middleware, route middleware and the route handler.

Middleware are invoked in the order they are added. If a response is returned no subsequent middleware/handler will execute.

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

  • server-side rendering apps to HTML
  • streaming server-sent events
  • hashing and JWT authentication
  • logging requests
  • caching responses

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

next: () => Promise<Response>

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 editing headers or 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:

router.use(async (_, next) => {
    const response = await next();
    if (!response) return new Response("Would you look at that? Nothing's here!", { status: 404 });
});
router.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. The configurable cacher middleware provides drop in handler memoization and response caching for your routes.

router.addRoute("/get-time", Peko.cacher({ itemLifetime: 5000 }), () => new Response(Date.now()));

The cacher stores response items in memory by default, but it can be extended to use any key value storage by supplying the store options parameter.

import { Router, CacheItem, cacher } from "https://deno.land/x/peko/mod.ts"

const router = new Router();

const itemMap: Map<string, CacheItem> = new Map()

router.addRoute("/get-time", {
    middleware: cacher({ 
        itemLifetime: 5000,
        store: {
            get: (key) => itemMap.get(key),
            set: (key, value) => itemMap.set(key, value),
            delete: (key) => itemMap.delete(key)
        }
    }), 
    handler: () => new Response(Date.now())
})

And that's it! Check out the API docs for deeper info. Otherwise happy coding ๐Ÿค“

App showcase

PR to add your project ๐Ÿ™Œ

shineponics.org - smart-farming PaaS

  • Stack: React, Google Cloud Platform
  • Features: Google Sheet analytics, GCP email list, Markdown rendering
  • source

peko-auth.deno.dev - basic authentication demo

  • Stack: HTML5
  • Features: JWT-based auth
  • source

Note: lit-html and es6-string-css VS Code extensions recommended.

Deployment

  • Deno Deploy (fork and deploy the examples if you fancy ๐Ÿ’–)
  • Docker (coming soon...)

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.

Motivations

The modern JavaScript edge rocks because the client-server gap practically disappears. We can share modules across the client and cloud. If we want TS source we can emit JS. This eliminates much of the bloat in traditional JS server-side systems, increasing project simplicity while making our software faster and more efficient.

This is made possible by engines such as Deno that are built to the ECMAScript specification and can run TypeScript natively. UI libraries like Preact combined with htm offer lightning fast client-side hydration with a browser-friendly markup syntax.

If you are interested in contributing please submit a PR or get in contact ^^

Credits:

Chick logo from Twemoji

peko's People

Contributors

akumarujon avatar navtoj avatar sejori avatar tamo avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

peko's Issues

File watch handler that wraps SSE to emit events when files change.

This can be added as a route in dev env to broadcast file changes to client. The intention is that the client can then either simply refresh or reload the module that changed. However, the latter get's complicated when we consider that modules might be bundled before being sent to client so knowing which modules appear in a bundle to trigger a reload would be a challenge.

Angular-like HTML Templating

Once the arguments of the HTML templating function have been agreed to remain fixed (at least mostly). We could use .html files with Angular-like {{value}} templating instead of describing html inside of a string in .js files.

The main benefit of this is that developers can have proper syntax highlighting of their HTML templates.

Hot-Reload with file change emitter in ssrHandler (devMode)

Watch provided SSR module and it's local dependencies for changes then omit server-side event that triggers client to refresh/reload html.

Clients will need to utilise some JavaScript to subscribe to the Hot-Reload event. Simple and effective implementation would be to dynamically reimport modules and rerun hydration script. This can be made into an example.

Helpers subdirectory

  • getDeps
  • add...Routes
  • JWT validation & creation for auth
  • Observable initialisation & listeners for datastore

Middleware Setup

Expand Peko Route type to include middleware function and rewrite serve logic in mod.ts to pass request through provided middleware before handler.

    {
        route: "/api/parrotFcn",
        method: "POST",
        middleware: (request: Request) => auth(request),
        handler: async (request: Request) => await new Response(`Parrot sqwarks: ${JSON.stringify(request.body)}`)
    }

auth() would likely be coming from Helper library or is defined by developer.

server.addRoutes(router.routes) misses async routes

When starting a server with a separate router module it makes sense to use server.addRoutes(router.routes) but this means subsequent additions to the router's routes won't be seen by the server. Using server.routes = router.routes is a workaround but that ties all server routes to the router routes and will undo any previous server.addRoute() calls.

Peko/Store - Observable datastore connected to Server Events

I believe it makes sense to include a lightweight datastore/database wrapper in Peko. By default data would be stored in-memory but database-service connection functions can be provided to change this.

It would also be cool to make data updates trigger ServerEvents so that new data could be emitted to client apps in real-time applications.

Optional Bundling/Compiling

It has been suggested that relying solely on prefetching source modules in browser will lead to worse performance than bundling. I can see this being the case in larger projects with lots of separate module code to pull into the browser.

It may be appropriate to add an optional bundling parameter to the SSR handler in Peko so that a page's source module can bundled and stored within a bundles directory (e.g. stdout). This would then be referenced in the client hydration script instead of the source module.

We need to do a bit of research on how worthwhile this is in terms of performance. Thoughts?

Full API Documentation

Pretty self-explantory. Should add comments to types etc in order to have these auto-generated.

"TypeError: Cannot read property '__H' of undefined" in production bundles (preact/hooks is culprit)

If anyone has any thoughts on this issue they would be so appreciated. To reproduce simply run the default template with $ docker-compose up and navigate to the about page. You should see this appear as an error in the browser console or in the Deno process logs in terminal. I've tried using many different version of Preact and they all seem to have this issue.

Potential fix would be to try and swap out the Deno bundler for a custom bundler like Bundler or Denopack.

Related to: preactjs/preact#2690

Remove Deno namespace references

Remove all references to the global Deno object everywhere in source code except tests.
This is so the library can be used in an engine-agnostic fashion.

`hostname` passed to `Server` does not apply to `stdServer`

The server created in the code below listens on http://0.0.0.0:7777 when it should listen on http://debug:7777.

const server = new Server({ hostname: 'debug' });

The parameter passed to the constructor is properly set on the class instance.
https://github.com/sebringrose/peko/blob/ff9f475f9c3101e8b21848899082c94a624236f7/server.ts#L31-L34
https://github.com/sebringrose/peko/blob/ff9f475f9c3101e8b21848899082c94a624236f7/server.ts#L39
However, this.hostname is not used when the stdServer is created.
https://github.com/sebringrose/peko/blob/ff9f475f9c3101e8b21848899082c94a624236f7/server.ts#L72-L76

calling next without `await` breaks cascade.

The cascade order is broken when calling next without awaiting it. It's very hard ot figure out exactly what's going wrong here but I've narrowed it down to the calling of next without await in a middleware.

You can see the response from a middleware/handler is lost and the cascade continues beyond what it should.

Fix Vue Example

The Vue example fails with the following error:

Error: On-the-fly template compilation is not supported in the ESM build of @vue/server-renderer. All templates must be pre-compiled into render functions.

Example Structure

Move preact example code ("example.js" file and "exampleSrc" dir) into a new "examples" directory so that future examples can be housed alongside. These files should be put within "examples/preact" and maybe renamed for clarity.

It would also be good to have a preact TypeScript example file as well as the existing JavaScript one to demonstrate using Peko and Preact with TypeScript.

General-Purpose HTML Rendering

As well as using the SSR handler for complete HTML page rendering it could also be used to serve chunks of HTML that can be loaded into a variety of web applications. Therefore it makes sense to restructure some of the code so that this usecase is more intuitive and we don't have to hack it into the existing system.

Renaming the "addPageRoute" function to "addHTMLRoute" and redefining the "PekoPageRouteData" type to be more general-purpose seem like the right course of action to me.

error from you example preact : deno version 1.24.3

i clone you example , but i get error
i use deno latest : Deno 1.24.3
d:\1. BELAJAR HTML\denoPRO\MAINAN\peko> deno run --allow-net --allow-env --allow-read --watch examples/preact/app.ts

and get error for Example preact folder

error message:
error: Uncaught (in promise) Error: The filename, directory name, or volume label syntax is incorrect. (os error 123), readdir '/D:/1.%20BELAJAR%20HTML/denoPRO/MAINAN/peko/examples/preact/src' for await (const dirEntry of Deno.readDir(path)) {

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.