- 0.1.0Latest
- 0.1.1
- 0.1.2
- 0.1.3
- 0.2.0
- 0.2.1
- 0.2.2
- 0.3.0
- 0.4.0
- 0.4.1
- 0.4.2
- 0.4.3
- 0.5.0
- 0.5.1
- 0.5.2
- 0.5.3
- 0.5.4
- 0.6.0
- 0.6.1
- 1.0.0
- 1.1.0
- 1.2.0
- 1.2.1
- 1.2.2
- 1.2.3
- 1.2.4
- 1.2.5
- 1.3.0
- 1.3.1
- 1.3.2
- 2.2.0
- 2.1.0
- 2.0.0
- 2.0.0-beta
- 1.9.0
- 1.8.3
- 1.8.2
- 1.8.1
- 1.8.0
- 1.7.5
- 1.7.4
- 1.7.3
- 1.7.2
- 1.7.1
- 1.7.0
- 1.6.0
- 1.5.3
- 1.5.2
- 1.5.1
- 1.5.0
- 1.4.1
- 1.4.0
- 1.3.5
- 1.3.4
- 1.3.3
- v.1.3.2
- v1.3.1
- v1.3.0
- v1.2.5
- v1.2.4
- v1.2.3
- v1.2.2
- v1.2.1
- v1.2.0
- v1.1.0
- v1.0.0
- v0.6.1
- v0.6.0
- v0.5.4
- v0.5.3
- v0.5.2
- v0.5.1
- v0.5
- v0.4.3
- v0.4.2
- v0.4.1
- v0.4.0
- v0.3.0
- v0.2.2
- v0.2.1
- v0.2.0
- v0.1.3
- v0.1.2
- v0.1.1
- v0.1.0
Peko
Server Routing Request handling Response caching
Philosophy
Client-edge synergy - Share modules for rendering + hydration and reuse classes for simpler development.
Production-ready backend - Cascading middleware library built on stable Deno APIs, all tested.
Software minimalism - Uses native JavaScript APIs and Deno std library only.
Ease of adoption - Intuitive “Express-like” API with no file-system routing.
Any feature suggestions or code reviews are very welcome!
Examples
A secure and scalable webapp in one file 🧑💻🌠 Deployed 👉 here.
Try locally:
Deno is sick. Install it.
$ git clone https://github.com/sebringrose/peko.git
$ deno task start:dev
Note: Lit-html VS Code plugin recommended if using HTM & Preact.
Deployment
Instantly deploy from GitHub with Deno Deploy (fork and deploy the examples if you fancy 💖).
Overview
Server
The Server is the main class of Peko. It wraps Deno’s std/serve and holds all route and middleware data for request handling. Server.use
can be used to add global middleware like the popular Express and Koa frameworks.
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.use(Peko.logger(console.log));
server.addRoute("/hello", () => new Response("Hello world!"));
server.listen(7777, () => console.log("Peko server started - let's go!"));
Routing
Requests are matched to a mutable array of Routes. Routes are added and configured with their own middleware and handlers via the addRoute
, addRoutes
, removeRoute
or removeRoutes
server methods.
server.addRoute("/hello-log-headers", async (ctx, next) => { await next(); console.log(ctx.request.headers); }, () => new Response("Hello world!"));
server.addRoute({
route: "/hello-object-log-headers",
middleware: async (ctx, next) => { await next(); console.log(ctx.request.headers); }, // could also be an array of middleware
handler: () => new Response("Hello world!")
});
server.addRoutes([ /* array of route objects */ ]);
server.removeRoute("/hello-log-headers");
Request handling
Each route must have a handler
function that generates a Response. Upon receiving a request the server.requestHandler
will construct a RequestContext and pass it into global middleware, then route-specific middleware and finally the route handler. Global and route-specific 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.
We can design efficient logging/post-response operations by utilizing the middleware cascade. The server returns the response into previously called middleware that implement await next()
before responding to the client request. See the below snippet or middleware/logger.ts
for examples of utlizing the cascade. Returning a new Response
after awaiting next
will overwrite the response to be sent to the client.
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 });
}
});
The included staticHandler
, ssrHandler
and sseHandler
handlers can be plugged straight into routes and reduce boilerplate code for serving static assets, rendering HTML on the server or streaming CustomEvents for server-sent events respectively. There are also authentication, logging and caching middleware to cover the basic set of app requirements. See examples
for demo implementations. Of course, you can also create your own middleware or handlers and plug them into your routes.
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()));
The modern edge is cool 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 (if you do set up a React or Vue project please consider opening a PR to the examples). Simply plug your app-rendering logic into the Render function of an ssrHandler.
This is all made possible by powerful new JavaScript tools. 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 :D