Replies: 15 comments
-
|
Thinking about it, I could simply not invoke the |
Beta Was this translation helpful? Give feedback.
-
|
I think the most important part is not to come up with a API in advance. There is so many edge cases here it most important to stay nimble and allow experimentation. I have been thinking about a few different scenarios. One of which would be to check in But it is a pretty tricky one to do really well. Webpack server has a websocket (I think?) connection open to reload pages/modules. But to then also ideally reload a page that isn't server by webpack is going to be interesting. |
Beta Was this translation helpful? Give feedback.
-
|
I like the idea of using the fab dev server as a proxy to your existing server, since that's how it behaves in production. I can imagine issues when it comes to serving assets since your uncompiled assets may look very different to the fab compiled assets. So you may end up with very different paths. Probably this won't be an issue in most cases. |
Beta Was this translation helpful? Give feedback.
-
|
I've given this some thought and would like to share where I am at right now. Setting up the stageI'll focus solely on the DX of running a FAB with assets that are dynamically compiled by some user-space tooling, typically a development server like the one run by I'll also make some distinctions between:
FAB in uppercase letters always refer to the parts of the standard. Some definitions:
Doing it "right" is a dead endI started with the following design constraints:
I don't think it is practically possible to achieve this. Indeed, we are dealing with the following interfaces:
Because FAB's upstream interface is file-centric and FABs have so far been built "cold" for production use, the The real design challenge lies in this issue. The rest of the problems are comparatively trivial: setting up a watcher and an incremental compile step for the server part of a FAB is just engineering effort (and is partially done already with I had written the different design avenues I explored to fix or avoid the mismatch and why they are all a dead-end to me, but I removed it because it was a wall of text that led nowhere. I can share that on Discord. Loosening the design constraintsKnowing that a solution following the stated design constraints cannot be practically achieved, let's relieve some. The most critical one is the first:
Taking inspiration from recent developer toolingCurrently, there is a trend in developer tooling that purposefully does not follow that principle. Tools like Snowpack and Vite maximise the runtime behaviour between development mode (server through development server) and production mode (bundling the application into a folder of assets) in order to speed up development builds as much as possible. The differentiation is maximised because these tools separate compilation from bundling:
Let's take this as a source of inspiration: could we separate plugins between what is akin to compilation and was is akin to bundling? Starting with some examples:
Note that the purpose of a plugin is not always indicative of at which step the code runs. A plugin that inline assets in Making the purpose of a plugin explicitThe learning here is that different plugins have different purposes, and it is their purposes that determine whether they should run in development. One could already get a sense of that, with some plugins prefixed with I'd like to propose a significant change, but much less ambitious that what I had initially proposed when keeping all design constraints. to clearly separate plugins by their purpose in the config.
So, instead of having one Once the purpose of plugins is clear,
The description of the change is not very detailed. There are a lot of stuff to hammer out; it's just to start a discussion. |
Beta Was this translation helpful? Give feedback.
-
|
Thanks for this @yacinehmito. Lots to think about. Sorry it's taken me a couple of days to reply, I wanted to jot down a few points here and I think the best thing then is to set up a call to discuss it.
This is potentially an important distinction. FABs started life as a purely production-only artefact. In fact, the original design had no As a result, and attempting to make Initial thoughts
Love this delineation conceptually, but at first blush I can't think how exactly to capture that in code/tools.
This I'm less sure about. I think it might be useful to drill into a concrete example here, which I'll come back to.
Love this. I think the challenge would be mapping the internal plugins to this model in a way that allowed them the best dev experience. But as for user-specified plugins, particularly the
I think you might run into ordering issues here? Unless you can guarantee that all
Love it. Would never have thought of backing onto a folder, but it makes sense. Effectively, So I think we're definitely thinking in the same lines wrt tooling, but I want to drill into a particular example of a plugin that might drive out the compile/bundle idea:
|
Beta Was this translation helpful? Give feedback.
-
|
From our call:
This hit the nail on the head. There's so much stuff you could be doing with a little FAB server, so many things that currently need a full on Express/Hapi/Koa server (which aren't directly compatible with FABs) that would be awesome as little, shareable snippets of FAB server code, but if it's not easy to spin up & iterate on (independently of packaging & deploying) then it's not really a true "alternative" to existing JS server-side code. I have a bunch more notes but I think I'd rather jot down where my head is at about how to proceed: Probable
Alternatively, that could all be hidden behind a "smart" To start with, Potential
A "FAB Server" could run totally independently of a FAB, all it is is a dialect of a request/response interceptor that uses the Fetch API natively. But FABs would have zero-configuration support for a FAB Server. So you could think of your FAB-enriched app as being three parts:
Then, in Probable
That is to say, if a project has a This would mean that the server file has no access to any Also, potentially, we could reframe {
server: {
entry: "src/server.ts",
precompile: true, // or "webpack"
}
}That might be a nice way to reach out to folks with a fair bit of server-side code already, rather than the concept of a plugin-inside-a-plugin. More thoughtsI'll jump back in when I think of them but it's dinner time now yo! |
Beta Was this translation helpful? Give feedback.
-
|
I thought I might throw in my own perspective on this, not on how the problem should be solved but rather on how I'm currently using the plugin system. This may help you make decisions. I'm doing something different to the use cases you're describing and I'm kind of using the FAB Server as an alternative to express. This is all quite experimental and so I've made some weird decisions and using things in non-standard ways. My app is kind of like a CMS that runs on the edge with Cloudflare workers (it's not that at all but it's the closest thing). My insight is that a CMS is mostly reads, not writes. So it doesn't make sense to waste server time on rendering templates and hitting the database every read, instead it only renders templates on writes. Reads come directly from the Cloudflare KV store. I have a few different plugins:
The rest of my setup is basically the same as a static app. It's a series of templates rendered by eleventy, a SPA built in svelte, all built together and optimised by The The Build Runtime Right now, when I'm editing templates for the
Ideally each of these would work in a chain, so if I edit a template, it is built, then parcel detects the built template and builds the static site, and then fab detects the change in the built static site and builds the plugin, then builds the fab and restarts/hot reloads the server. I think re-thinking how FAB works to support more efficient development is a really good idea. And if doing that breaks my plugins thats not a big deal (since this is all experimental anyway). I just thought it might be helpful to get some insight into another use case. Have a good weekend! |
Beta Was this translation helpful? Give feedback.
-
|
Hey @taybenlor, this is so so cool! @geelen and I have talked about exactly this use-case so often. We just haven't found the time to actually build it. Do you have this somewhere publicly where we can take a look at it? Would love to see it. I am pretty sure that the case that you describe there is what we want to support as a minimum. I think it is easy to get stuck on how to support advanced use-cases like NextJS, but I think it would be wise to start supporting what Glen and I have started to call Static+. A mostly static deployment, but with some dynamic logic sprinkled in, exactly what you describe above. That is a use-case which has been very much underserved in the current market and we can make the biggest difference. If we can think of a way that may also support NextJS and friends in the future, that would be great. I'll have a think about this today and see if I can come up with a proposal. |
Beta Was this translation helpful? Give feedback.
-
Thanks! I genuinely think it's an interesting direction for web app development to go, especially as an architecture for "web apps on the edge". Unfortunately nothing I'm able to show publicly yet - but my MVP should be stood up in the next month or two and then I'd be happy to walk you through it. |
Beta Was this translation helpful? Give feedback.
-
|
Ok.. so here is my $0.02 on the subject. I absolutely loved @yacinehmito's Setting the Stage, so here is my version: The Purpose The Goal Now as Yacine has pointed out already the speed and closeness to production are impossible to achieve together. The closest to production is to run a full build, which is obviously not quick. So we are indeed building something that is imperfect, but can still be extremely valuable if we get it mostly right. If we look at what needs to happen there are a few broad steps.
What if all plugins had an optional The one thing that I am not too sure about is how the change detection/notification flow would go. First I thought it was from fab-dev-server to the plugins, but I think it would have to be the other way around. A plugin in dev mode would be able to watch a particular directory, or to spin up a dev server for the assets they are responsible for building. So to take @taybenlor's example with the build-time And when we get to things like NextJS dev servers, we could subscribe to notifications for update if they already have that functionality, or add the functionality if it doesn't. |
Beta Was this translation helpful? Give feedback.
-
|
@taybenlor this is super awesome! I love that you're using the Do you have any idea how long the turnaround time is for a @evanderkoogh maybe you missed my comment above? I describe something very similar to that #188 (comment) under "An Alternative Idea". Basically drills down into some That said, my chat with @yacinehmito kinda took a different direction. We were trying to think about how to give "runtime only" plugins a big iteration boost first, and potentially leave So while I'm pretty sure on the very next step (create a
Will keep noodling on it, but if you've got more suggestions/discussion, fire away! |
Beta Was this translation helpful? Give feedback.
-
I'm using Typescript for everything else, but decided to write the build step in JS because I didn't want to bother with compiling it. I was thinking of looking at that issue on getting build scripts in Typescript working. I think the T extends PluginArgs thing totally makes sense, you want the plugin to be able to construct its own version of the pipeline.
This makes a lot of sense to me. I think you're right that small server-side changes are probably the main use of the plugin system, so making that much easier is going to be great. I also think having a "FAB Server" spec would be super useful. I find the idea of building directly on top of CF primitives a bit worrying since it locks me into particular infrastructure. Having a spec also encourages other implementations on top of AWS, Azure, and Google Cloud. I see a FAB server as being the perfect glue between web microservices. For example my plan is to implement authentication using Auth0 and payment using Stripe. Eventually if I need atomic persistence I plan to use a web service for that as well. That means I have no need of a "real" backend - I can just have the FAB server glue together the various "APIs as a Service" that I use. PS: The types are so good in FAB, makes it really easy to quickly figure out what is going on. [Edit] Sorry missed this:
I haven't checked yet but I can get back to you when I next have some free time! |
Beta Was this translation helpful? Give feedback.
-
Yeah, effectively it'd be a "server-side service worker spec with cache", which just so happens to map nicely to CF Workers at the edge. If there's a true competitor to Workers down the track or just if you're not interested in edge compute you can run it anywhere. Same arguments for FABs proper, but for a server mindset.
So one of the advantages of Express and friends is the abundance of published middleware. APIs like: const middleware = require('some-api-service/middleware')
app.use(middleware)This is something I hit a while ago, I think I was maybe trying to support divjoy with server-side code provided by FABs. I think the Mailchimp SDK kinda dropped-in in Express but had a direct dependency on Whatever "FAB Server" spec we come up with, I think I'd like to design it with that kind of code-sharing in mind. In that respect, a hard dependency on building & deploying FABs is counter-productive, since you'd then need to convince people to use FABs (when there aren't good drop-in libraries) to get the critical mass to make drop-in libraries. On the other hand, a separate, modern (fetch-based) pure JS middleware definition is a bit lighter, and backwards compatible with an Express server. So I think I have enough to proceed but I have a few open PRs I need to look at today. Biggest question I'd like feedback on right now is: what to call this "new" thing? "FAB server" works I suppose, but anything better? Also, the API for accessing it. Atm we expose a default export that gets passed a import express from 'express'
import { MiddlewareUtils } from '@fab/server-spec'
import server from './fab-server.ts' // (still use the default export idea)
const app = express()
app.use(MiddlewareUtils.toExpressMiddleware(server))
app.listen()Is that... good enough? Or should we be encapsulating things further? |
Beta Was this translation helpful? Give feedback.
-
|
👍 I really like the idea of having a lightweight spec that is seperate from FABs. Especially if the spec is essentially just a minimal API, that way people doing any kind of serverless JS can use the spec. I don’t really know much about Edit: actually looks like I misunderstood the intentions of Deno. What you’re doing here is more like (as you’ve stated) what cloudflare is doing with V8 Isolates. |
Beta Was this translation helpful? Give feedback.
-
|
Yeah, Deno looks like it is very much a nodejs alternative, with native typescript, build on more web APIs and without relying on a central NPM registry. Theoretically it should be possible to run FAB in Deno, but the last time I tried to use one of their pre-release candidates the fetch stuff didn't follow the web standard in a bunch of major ways and I gave up pretty quickly. One schools open up back in Melbourne and I don't know what I should do with my time anymore I may find the time to give it a go again in my then copious spare time :D |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
Was just considering the DX of working on FAB-level plugins after chatting to a few folks this week about the potential for this "FAB server layer" to form a really interesting layer of functionality & caching, between your user & your upstream API server. Starting with this thought bubble, but I realised that the workflow for building this kinda sucks:
Let's say you update one of your FAB plugins, and want to test it:
yarn build:fabto build both your app and the FAB. Even for simple apps, that takes a little while.yarn fab:buildwill do it and is fairly quick, but then you do need to rekick yourfab serve fab.zipprocess and refresh the browser.Was thinking a different workflow, but I wanted to put it out there to see what people thought of it before I lock down an api:
This would read your
fab.config.json5file, find all the plugins you're using, ignore (somehow, see below)input-{static,nextjs,sapper}, and things likeplugin-rewire-assets, and actuallyplugin-render-html, since they're really tied to the compiled FAB. But it would live-reload the remaining plugins, and forward requests ontohttp://localhost:3000.This should mean you load
http://localhost:3001as if it's your normal dev environment, but requests are moving through the FAB server code, meaning things like/api/graphqlcould be caught and all their logic iterated on. Hot module loading should still work with Webpack's dev server for client-side changes.I'd need to stub out the
cachemodule that I'm planning on introducing as part of RC2, which actually gives me a good way to dev on that, so I think this is going to happen.Plus, I want to run
fab.dev/blogas a server-rendered React Notion proxy so I can start to wean myself off gatsby. This would be a good way to test that.Beta Was this translation helpful? Give feedback.
All reactions