pylon icon indicating copy to clipboard operation
pylon copied to clipboard

[Feature Request ] - Pylon without web server.

Open ShanonJackson opened this issue 1 year ago • 5 comments

Is your feature request related to a problem? Please describe. Would love access to opportunity to use pylon in a way that still allows me native control of the underlying http server.

const app = new Hono(<opportunity to configure here>)
// <various app logic 
Bun.serve({
	port: Number(process.env.PORT || 3001),
	idleTimeout: 120,
	fetch: (request, ctx) => {
	        // some programmatic logic here.
	        // opportunity to use pylon here.
		return app.fetch(request, ctx);
	},
});

This has the following advantages:

  • Potential to use pylon in other web frameworks that aren't Hono.
  • Potential for people to integrate pylon into brownfield apps more easily.
  • Simplifies documentation / maintenance because all documentation relating to the "web server" component can be removed or kept for backwards compatibility in another section; Or 'in parallel compatibility'.
  • Potential to configure idleTimeout (https://bun.sh/docs/api/http#idletimeout) which prevents slow loris attack and solve other problems.
  • Potential to configure PORT as environment variable including the environment variables name which simplifies the pylon command.
  • Potential to use different commands for dev I.E
"dev": "pylon dev -c \"bun run .pylon/index.js\" --client --client-port 3003 --client-path gqty\\index.ts",
// becomes
"dev": "bun run --watch ./src/server.ts"
  • Simplifies docker entry point command.
  • Potential to use pylon (graphql) in environments outside of http I.E udp, LoRA, etc.

The summary of above is it would provide people to separate "pylon" from the underlying "runtime" and "web framework" allowing more flexibility to developers.

Issues and simplifications

export const graphql = { Query: { hello: () => 'Hello World'}, Mutation: {} }

This syntax would potentially need to expose a type of a function that matches the app.fetch signature from Hono. I see this though as an improvement because it would allow people to create different adapters for different web frameworks. I.E

export const graphql = { Query: { hello: () => 'Hello World'}, Mutation: {} } 
export default graphql // existing setup non-breaking change stays the same.

// web server control way being proposed 
import { adapter } from "pylon/hono"; 
export const graphql = adapter({ Query: { hello: () => 'Hello World'}, Mutation: {} });

Which leaves room for express/elysia/node/etc adapters.

Adapters would setup a AsyncLocalStorage context, allowing developers to set getContext and potential to use that (request storage context) for other properties/state I.E tracing etc.

resloves

https://github.com/getcronit/pylon/issues/9 https://github.com/getcronit/pylon/issues/21 (because can leverage existing auth strategy's in underlying web framework)

ShanonJackson avatar Nov 07 '24 22:11 ShanonJackson

Hi @ShanonJackson!

Thank you for your interesting request. I need to give this deeper thought to determine how a more "general" approach would be feasible, considering Pylon aims to be a framework that simplifies development rather than a universal library for all frameworks.

Here's what I can say for now:

  • Pylon's dev command is designed this way because it relies on a prebuild step for schema and client generation. That's why you need to execute .pylon/index.js. I'm not sure if it's technically possible to change that at this point.
  • You already have control over the "server." The proposed Bun.serve command works out of the box, so you can configure idleTimeout, PORT as an environment variable, etc.

Could you elaborate on how this would fix issue #9?

schettn avatar Nov 09 '24 09:11 schettn

for #9

app.post("/2024-10-10/graphql", adapter({
    Query: {
          resource: () => "hello 2024-10-10"
     }
}, {output: "2024-10-10.schema.graphql"});
app.post("/graphql", adapter({
    Query: {
          resource: () => "hello original"
     }
}, {output: "schema.graphql" /* default */});

and via spread to override a specific resource without rewriting the whole graphql constant.

const gql = graphql({Query: {resource: () => "hello non versioned"});
app.post("/2024-10-10/graphql", adapter({...gql, Query: {...gql.Query, resource: "hello 2024-10-10"}})); // with "resource" overridden but everything else same.
app.post("/graphql", adapter(gql));

This way you can create new versions of specific resources and/or entire new graphql schemas using whatever version system you desire. I.E standard api numerical based versioning or by date.

Obivously above syntax requires outputting

Pylon's dev command is designed this way because it relies on a prebuild step for schema and client generation

For this yes, the intention was to decouple this "prebuild step" from the dev command itself and run it as an entirely separate command.

I.E pylon generate --watch dev would be concurrently watch and serve. Not that I'm a fan of saying "here they do x" but in most code gen based tooling they have decoupled watch commands.

You already have control over the "server." The proposed Bun.serve

Can you show me the syntax for this?

ShanonJackson avatar Nov 09 '24 22:11 ShanonJackson

Can you show me the syntax for this?

import {app} from '@getcronit/pylon'

export const graphql = {
  Query: {
    test: () => 'Hello, world!'
  }
}

export default app

const server = Bun.serve({
  fetch: app.fetch,
  port: 4000,
  idleTimeout: 120
})

console.log(`Server running at ${server.url}`)

schettn avatar Nov 11 '24 13:11 schettn

for #9

#9 is not just about configuring date based routes, but rather automatic backwards compatible versioning handled by Pylon. Related to #8.

So for example when you release a API with a a test query and later remove the test query, it will still be available in old version endpoint, but no longer in latest. This should then be the case with all breaking changes that you introduce in your pylon application.

schettn avatar Nov 11 '24 13:11 schettn

Hi @ShanonJackson.

I just released a new feature that allows setting up the graphql routes manually.

This is mainly used for dependency mocking during test, but is also the first step to be more framework agnostic.

https://pylon.cronit.io/docs/guides/testing#optional-mocking-dependencies

schettn avatar Nov 26 '24 08:11 schettn