Life after Meteor

I guess most of us here are happy with life with Meteor, so you might not get the perspective you are looking for.

I personally tried the stack you and macro explored, and I did a couple of projects with NextJS and some with pure Webpack/Express, and I found myself redoing a lot of leg work that is not business-related and that didn’t add any value to my clients, that was not fun, so I kept going with Meteor whenever we can, it was way more pleasant to work with.

Meteor is getting a lot of momentum now, so I think you should reconsider it and try using with it with more NPM packages and one of the dominant view layers (React, Vue or Svelte.) especially now that the serverless and GraphQL hype are slowing down. We are building a lot of PWAs with Meteor and they’re really fun to create and folks are creating more libraries to make creating PWAs easier with Meteor.

Also, make sure to attend the upcoming Meteor Impact there are several workshops pertaining to PWAs :slight_smile:

4 Likes

online shops, mobile apps. in general custom projects for customers, so a lot of different products. So my focus was on a simple, flexible stack where we could swap team members easily, so it was important to keep things simpel and easy to understand.

so nextjs was a good fit, because we could swap the backend easily; some projects might need a backend, some don’t and others might use a third-party backend.

graphql was a good fit, because you can basically look the schema and understand the app’s domain model.

and prisma was a good choice because it

  • has also a schema that defines the database
  • with nexus-plugin-prisma you can create type-safe graphql resolvers based on your db schema, you can even auto-generate all crud resolvers with pagination, sorting and filtering, that can be consumed by something like react-admin

in general i like that it keeps me sane by providing nearly 100% type safety and transparency, makes a lot of fun to develop.

the only drawback so far is still a too basic migration system, but its about to get better.

also authentification and authorization is something that you maybe have to add to the mix and you can do mistakes there.

2 Likes

Yes, there are few actually

  1. Thingking about Meteor 2.0 🤔
  2. Stuff to be removed in Meteor 2.0
  3. Does It Make Sense to Start a Meteor 2.0 to Retain "Classic Meteor"?

@filipenevola

3 Likes

Meteor v2 (currently in development) will include HMR and related improvements for sure. Don’t expect much beyond that.

2 Likes

Loved meteor

I’m now using

  • node
  • mongoose
  • apollo server
  • apollo client
  • create react app
  • accountsjs
  • mongodb.com
  • dataloader

on most projects. My big thing with meteor were the accounts and accountsjs is the perfect substitute… i wish the project got some more financial backing from a big player that uses it.

8 Likes

Good to hear from you again! I had a similar experience:

3 Likes

:flushed:

@gadicc

I started looking at Meteor in late 2014, getting into it a bit in 2015, more in 2016, and making a decent sized app in late 2016/early 2017. Meteor was so fun and so fast! I’ve not really worked on a Meteor project with a team. In Spring of 2017 I took a job and the app-development aspects of it have been React on the front-end and Node/Express on the backend. Over the last 4 years I’ve still added to the Meteor app on weekends here and there.

It can’t be overstated how fast and fun Meteor can be: sometimes I could conceive of and make a feature in the span of a few hours while waiting for a ferry! Switching between the front-end and back-end so quickly is a tonne of fun.

An aside on GraphQL:
At my main job, we switched to Apollo/GraphQL about 15 months ago—a decision I helped steer. I’ve been thinking a lot about devolving the GraphQL API into a REST API. Not many arguments against going back to REST from the team. I won’t go into the GraphQL drawbacks too much here, but the highlights can be summed up by throwing out phrases like developer-overhead, complexity, authorization, optimization, and unnecessary/long-winded data-structure migrations. Using GraphQL on the front-end is pretty nice, though no more pleasurable than a purpose-built REST endpoint with decent documentation and fetched using a satisfying useApi hook :slight_smile:

The benefits of Meteor for me have mostly gone away over time as the rest of the JavaScript world has caught up on build-tools, but Meteor has become less relevant for me personally because of its heavy reliance on Mongo. You can use it without Mongo, of course, but then it’s not much different from a regular server/front-end, at least for my cases. The ability to use the same form validation and have shared utility functions on both the client and server is pretty amazing in Meteor, and it’s something I’ve never seen handled by a web-app+API that beats imports/lib. If I were to rebuild the app I made 4 years ago using the technologies available today and with my current ways of thinking, I’d probably just do a Koajs REST API with a stand-alone React front-end SPA/PWA.

Would you have any recommendations for Tiny for where Meteor could go? I’ve been mostly reviewing PRs lately, so brain is in a different headspace :slight_smile:

3 Likes

I find your experience with GraphQL interesting, what did you’ve before that, REST? I’d appreciate it if you could share an article or more details on those internal discussions.

We’ve several apps deployed like that, but with Meteor as a backend, SimpleDDP (and sometimes REST) for the data, and React SPA at the front. So, in these apps, we’re using Meteor as a backend and build tool only.

Why? many battle-tested packages, authentication, real-time when needed, simple RPC with DDP methods, Atmosphere packages, configured build-tool, development server, Galaxy/MUP and APM tooling for production. Also, the bi-directional dedicated socket connection comes handy in many cases, and typically you’ve one socket open for notifications/subscriptions etc, so re-using that connection for data make sense as well. However, if all you need is basic Rest API and SQL, then I think KoaJS/ExpressJS are great choices as well.

1 Like

Hi @alawi

I think I should write a blog post or something about our discoveries with GraphQL as I’ve found it quite difficult to find similar opinions about GraphQL: the sentiment seems to me to be along the lines of “use GraphQL… end of discussion” on the internet, without much nuance.

GraphQL penalties:

On developer-overhead and complexity… to expand a REST API server with more functionality—or even to get it going in the first place—there’s not a lot of incremental wiring one has to do to conform to the rules of the server. With something like Apollo Server though, you have the schema itself, also resolvers, also models, also data-loaders. Knowing what’s wired up to what might seem like you could learn it and then get it, but it’s actually a lot to hold in a brain at once, especially because it’s not the same case for all uses—sometimes resolvers aren’t needed because they come from the data itself, sometimes resolvers are needed to deal with it, sometimes resolvers call models calling data-loaders calling models. With REST it’s all there in your endpoint. The complexity and boilerplate lead to more dev time for sure… even the simpler things like schema is stuff you don’t have to do with a typical REST endpoint

On authorization
GraphQL is easy to add authorization to at the mutation level—just like a PATCH/PUT/DELETE—but adding it at the query level is… I would actually say impossible. Hear me out… you’re traversing a graph, so you can enter a resolver via multiple ways; what if you need to lock down the access to the authorization of the user who is requesting the entity in one of those ways but via the entity itself in another of those ways? This might sound crazy, but it’s actually not unreasonable, especially when you consider user-administration where one might have a different set of permissions for themselves compares with a user they can administer. Any solution to this makes my brain explode. All authorization examples I’ve seen are more trivial… generally have no additional logic on them like “user has x permission on y entity”

On optimization
In a REST endpoint, you can highly optimize the endpoint for the specific task at hand. Here’s a weird example to obfuscate our use-case :slight_smile: Let’s say the app is for a heat-ventilation/air-conditioning company and for a given user of the app, you want to see all of the heat exchange date settings across the office buildings they have access to… something like:

query {
  user {
    officeBuildings {
      id
      name
      hvacSystems {
        id
        name
        settings {
          heatExchange {
            startDate
            endDate
          }
        }
      }
    }
  }
}

Using data-loaders in GraphQL, you’d probably end up:

  1. Getting the user requesting the service
  2. Finding all officeBuildings that the user has access to
  3. Finding all hvacSystems across all of the office buildings
  4. Finding all heatExchange settings across all of the different hvacSystems

It’s a necessary waterfall. In a REST endpoint, that could be done in 1 step… in reality I’d probably put the first step into middleware, so instead it’s a 2-step process in REST. You could add multiple entry points to overcome this if it’s a common use-case in GraphQL, but then you’re increasing the complexity even further… maybe authorization too :smiley:

On unnecessary/long-winded data-structure migrations:
This one might be more self-evident if you think about the case when the schema has to change as business-logic changes. e.g. Maybe in the case above, hvacSystems no longer exist as an entity, maybe it was the wrong way of conceptualizing your business, and instead you have independent components that are serviced. You need a lot of coordination with clients to rip that out. It might be work in REST to do the same, but maybe not… a lot of the time in REST you give a request and it tells you some subset of data. For times when the data structures being returned from REST don’t change, you could rewrite the way your endpoint functions internally while still maintaining the same data response. This might sound fringe, but there have been multiple migrations over the last year like this that cost a fair bit of extra development and backwards/forwards compatability woes for our mobile app, since you can’t control what versions of a mobile app are out there. Often change doesn’t even need to happen on clients but since there’s a schema mapping clients to a server’s resolvers, it can be necessary (or really hacky/complex to work around it).


I like the simplicity and flexibility of method calls in Meteor. We used REST before GraphQL. I treat REST more like an RPC server if we have control over the clients. Anti-pattern, but I’m anything but puritanical.

Interesting having Meteor as the backend/build! How do you distribute/serve it all?

5 Likes

Thanks for sharing! I will reread your post several times, and I think it is worthy of a blog post.

With regards to Meteor as build/backend setup. The backend/client are deployed independently. We have implemented a version checker (similar to one the Meteor web client has) using React/SimpleDDP to keep the client/server in-sync. The data layer is RPC style using Meteor methods and subscriptions are used only when required. It is the simplest API you can have, literally a single line JS method call, you will get access to the many backend Atmosphere packages (authentication, rate limiter, Method validation, etc) as well as real-time pub/sub when needed.

The server can be deployed on Galaxy or any other Node host. For the client, we used a static CRA site deployed on Surge. The application public API is exposed as REST.

It is a great setup if you are looking for something similar to a JAMStack with a Meteor backend.

1 Like

I use swydo:ddp-apollo, and my query resolvers receive the userid of the current logged-in user in the context param:

        my_query: (parent, args, context) => {
            const {userId} = context;

Then I can check if the userid is authorized for that query.

However I’m really asking this to learn something from you. You probably know about this and I’m curious what the weaknesses are of this pattern for authorization. Please let me know.

1 Like

I tried Phoenix because I heard great things about it, build an app that’s in production. Real-time datalayer is super simple to implement, and scalable. A lot of Rails structure for your project, migrations, easy api building, etc.

However, when starting new projects I keep reverting back to Meteor. The build process is awfully, terribly slow, but still it is the fastest way to build anything. Methods are api’s on steroids - there is nothing better in the JS world -, auth layer is so incredibly easy, and if Meteor is getting in your way just use plain Node.

Just don’t use publications, or publish very small datasets, and expect to wait a lot for slow rebuilds. But you get that time back in productivity in writing code.

Meteor 2.0 will fix the slow rebuilds, it will be awesome and put Meteor back on the map. Now imho it only needs decoupling from Mongo.

1 Like

Thanks for asking, @vikr00001!

Do you have different permissions for each user? I haven’t seen any authorization systems out there really beyond things like a single permission without qualification, e.g. isAdmin kinds of permissions. We pass a user object into the context, but we don’t include the associated authorization, as it would be too big to put into our middleware. A given user might have around 7000 permissions, as a permission might be “can perform this particular action on this particular entity”: we might need to let them view user 359’s status but not view user 598’s status, and we might need to allow them to view user 598’s email address but not user 359’s email address… that’s not our use-case, but things along those lines.

The reason I would jump to “impossible” or at the very least perpetually head-scratching, is that we might have situations like the following, where the user-requesting has access to Beth’s email, like so

query {
  user {
    friends(name: "Beth") {
      id
      email
    }
  }
}

we’d be tempted to say that the resolver for Beth’s properties like “email” be based on the allowance of the ctx.userId user, but consider the following:

query {
  user {
    friends(name: "Joe") {
      id
      email
      friends(name: "Beth") {
        id
        email
      }
    }
  }
}

… what if Joe can’t see Beth’s email but the ctx.user.id can… should we show Beth’s email? I think the answer might be more like “it depends” or “maybe”. It confuses me further to determine what we should do in this situation assuming we’ve got the above-two worked out:

query {
  users(name: "Joe") {
    friends(id: $theUserIdFromOurContext) {
      id
      email
      friends(name: "Beth") {
        id
        email
      }
    }
  }
}

Maybe we need to take the intersection of the immediate parent and the ctx.user.id requesting it? Maybe not? Maybe we have to consider all parents?

This is just a toy example, but things along these lines does come up when we have administrators that can administer other users’ accounts.

Thanks for this info. It seems like with 7000 permissions, there’s no way to get them all into an API call, and they’d have to be determined on the server regardless of db layer. Is that a reasonable way of looking at this by any chance?

P.S. I wonder if a graph db like neo4j would be relevant in any way to your use case?

Most people that say that have no clue what GraphQL should be used for. I today’s world where one is rather leaning towards decentralized approach and microservices I hardly see use cases where queries across different types of databases need to be joined.

I absolutely agree with the penalties.

We have the same setup, mainly for scaling purposes. Our Meteor FE can take over 1000 concurrent users whereas the Meteor BE server can take 6-7 depending on the complexity of the tasks on the smallest AWS Fargate server.

Works very well so far

1 Like

As always in life, emphasis is on: “make sure you’re choosing the right tool for the right job”