Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RFC: Fullstack and isomorphic package (work in progress) #14

Open
eric-burel opened this issue Oct 30, 2020 · 1 comment
Open

RFC: Fullstack and isomorphic package (work in progress) #14

eric-burel opened this issue Oct 30, 2020 · 1 comment
Assignees
Labels

Comments

@eric-burel
Copy link
Collaborator

eric-burel commented Oct 30, 2020

Describe the problem you want to solve or enhancement you want to bring

Ability to write full-stack "feature-oriented" packages with both server and client features, in NPM, as we are used to do in Meteor.

@vulcanjs/graphql is a good example of this, as it generates the graphql backend, the client fragments, and both are very tied.

Define relevant concepts

Full-stack package: A package that export server-specific code and client-specific code. But it could apply to any package that expose different code depending on various environment (server, mobile, client).

Meteor packages are example of fullstack package with their client and server logic.

This is different from just having code that works everywhere, like a low-level library. We are talking about code that is actually specific to the env (calling window on the client for instance).

We should be able to do something like import createServer from "@vulcanjs/graphql/server", import someHook from "@vulcanjs/graphql/client"and import someSharedCode from "@vulcanjs/graphql"

Isomorphic import: Using a fullstack package transparently.

Example: a full-stack package could expose @vulcanjs/graphql/server and @vulcanjs/graphql/client. An isomorphic import would be import foobar from "@vulcanjs/graphql" and let the the build system add /client or /server depending on the context.

Meteor is also doing that.

Limitation: the text editor can't know the environment where the code is run. So, for static typing, you have to explose a generic version. Meteor is subject to this limitation.
Blitz.js has something similar, but with "unbalanced isomorphism", limited to how they consume the server using a custom query system. The typing is coming from the server, that contains the actual logic, as the client version is just a dull wrapper that does an HTTP call.

True isomorphism: you write a single piece of code and let the build system modify it depending on the execution environment. Next pages fall into this category. getServerSideProps and getStaticProps functions exported in a page are server-only methods. The rest React (so client but also compatible with server-side rendering). At build time, those methods are removed from the client bundle.

This is something we would want to achieve for Vulcan model's:

const Foo = createModel({
   schema: {
      foobar: {
         type: string; // both client and server, to render form, generate the gql schema etc.
         resolver : () => {...} // field resolver, server-only
         onCreate: () => {...} // server-only
      }
   },
  queries: { ... },
  mutations:  { ... } // CRUD operations, server-only
})     

In this example, the schema mixes shared elements, like the type for "Foo.foobar", and server only methods like the graphql resolver.
The power of Vulcan is to let you write only one schema with everything. The limitation is that you don't want the client bundle to contain server code, and sometimes vice-versa. You can bypass this with ugly require or playing around with GraphQL context, but only so much.

Related

Describe your solution

First step is implementing full-stack packages.

Then isomorphic import could be kept as a "magic" helper specific to Vulcan. But it is not mandatory (like if you create an Express server and don't use weback, you just do import foobar from "@vulcanjs/graphql/server" as usual). This feature is more comfort for people used to Meteor.

Questions to the community:

For full-stack package: Multi entry Webpack export/TypeScript. It should not be too hard, but I've never done that before, there are a lot of moving pieces. So help welcome!

For isomorphic import: there is a demo package multi-env that does exactly this. The way it works, it imports either index.server or index.client, using Webpack magic.

For the text editor autocompletion, or common code, we can also add an index that exports both. That's a prototype so again help and feedback welcome.

A good example to get started is the graphql package, that contains both server and client logic.

For true isomorphism: it's not easy at all to mix server and client stuff at the same place.

  • is it even a good idea? MVC architecture would basically force you to explode your model in multiple pieces and bypass this issue.
  • it could be easier to have multiple named exports with a convention, like Next does.
    For example, in foobar.ts:
export const schema = { foobar: {...} } // the common part
export const fieldResolvers = {foobar: {...}} // server-only. We have to repeat the field names
export const resolvers = {... } // server-only
// etc.

And then you create the full model using some build time magic. This is still a bit blurry, that's the part that require the most thinking.

@eric-burel eric-burel added the RFC label Oct 30, 2020
@eric-burel
Copy link
Collaborator Author

Next.js has it's own tree shaking function for removing SSR code from the client bundle: vercel/next.js#27552
We could do something similar to remove resolver field from schemas client-side.

@eric-burel eric-burel self-assigned this Aug 9, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant