Gatsby: Building apps with gatsby.js: What are the drawbacks?

Created on 28 Sep 2017  ·  37Comments  ·  Source: gatsbyjs/gatsby

Hi,

I'm thinking about using gatsby.js to build web apps and then serve them through AWS S3 and CloudFront.

Are there any problems I likely would run into compared to creating a node.js app?

To use gatsby.js seems much simpler and this way I can easily integrate my content website with my apps.

Best regards,
Daniel

question or discussion

Most helpful comment

Woah woah woah @barbush I really do not want you policing questions like this. If it's a terrible question (which this is not) then it's better to ignore it then telling the person off. Please do not respond to questions like this again.

@bolus to your question. Gatsby is designed to be fairly similar to create react app and other webpack/react setups. So it's perfectly capable of being used for building web apps. If you look in the examples directory, there's a redux example. People have used it with Apollo reportedly quite successfully. Relay is atm not possible to use with gatsby as our graphql usage conflicts with theirs but I'm pretty sure this will be easy to work around in the future.

The main drawback that I know of is it does assume you're building "pages" so if you're building more of a straight up app without pages then it doesn't give you much and would also limit your freedom somewhat. In that case you'd be better off using a more vanilla setup like CRA.

But if you are building "pages" gatsby is great as you get auto code splitting and static SSR for fast booting of the app.

I'd love to write a more formal tradeoffs document at some point but am happy to take questions here in the meantime.

All 37 comments

Thanks for your feedback, @barbush. I didn't realize 68 words would be too much.

The headline basically says all: What drawbacks are there when I build an app with gatsby.js?

I know it's possible to build an app. My question is - as gatsby.js is optimized for static website generation - what drawbacks are there? Is there anything that is going to bite me in the ass later on?
Seems pretty specific to me.

Woah woah woah @barbush I really do not want you policing questions like this. If it's a terrible question (which this is not) then it's better to ignore it then telling the person off. Please do not respond to questions like this again.

@bolus to your question. Gatsby is designed to be fairly similar to create react app and other webpack/react setups. So it's perfectly capable of being used for building web apps. If you look in the examples directory, there's a redux example. People have used it with Apollo reportedly quite successfully. Relay is atm not possible to use with gatsby as our graphql usage conflicts with theirs but I'm pretty sure this will be easy to work around in the future.

The main drawback that I know of is it does assume you're building "pages" so if you're building more of a straight up app without pages then it doesn't give you much and would also limit your freedom somewhat. In that case you'd be better off using a more vanilla setup like CRA.

But if you are building "pages" gatsby is great as you get auto code splitting and static SSR for fast booting of the app.

I'd love to write a more formal tradeoffs document at some point but am happy to take questions here in the meantime.

@KyleAMathews: Thank you, that is exactly what I was looking for.

I'm planning to create a content-driven website (a blog, sales pages, documentation, etc.) and for simplicity, I'd like to host a few small single page apps on the same domain.

Looks like Gatsby is ideal for this.

would also limit your freedom somewhat

It's probably no problem for my use case, but can you tell me what limitations I can expect, and how difficult it would be to work around them?

Thanks for creating Gatsby, btw., Gatsby looks really awesome! :)

but can you tell me what limitations I can expect, and how difficult it would be to work around them?

Gatsby tries to be as simple and unassuming as possible so you probably shouldn't run into limitations especially as you're doing content/page websites which is exactly what Gatsby is designed for.

Gatsby is designed to blend web app and site ideas to create what's in our minds the ideal development & production tool for building really fast sites as easily as possible.

You'll only run into trouble when you want to use React in ways that break out of the "page" model — e.g. more freeform apps. But even there, Gatsby has an escape hatch which lets you easily embed apps within a larger site https://www.gatsbyjs.org/docs/creating-and-modifying-pages/#creating-client-only-routes

Sounds perfect, thanks again!

hi @KyleAMathews and @bolus

commenting here bc of the context instead of opening a new issue heheh

what happens if inside my /app (that is a client only route), I want to create a SPA (login / logout / dashboard), I suppose I need to create a new router inside it, is that correct?

what do you recommend in this usecase @KyleAMathews , is it possible? or would be better to use a _more vanilla approach_ as you stated?

thank you

@fernandes checkout https://www.gatsbyjs.org/docs/creating-and-modifying-pages/#creating-client-only-routes — let us know if you have more questions!

hi, @KyleAMathews thank you for the quick reply

I played with Gatsby all Saturday, checked this example and redux one... Sorry about not making myself so bright, I could have given more information about it

What I'm trying to do, get my: https://github.com/fernandes/react-boilerplate and put it inside /app as a client only path

this boilerplate is composed by:
react / redux (with hot reloading) / react router redux

I'm not so experienced with JS; maybe it's just a detail I'm missing.. thank you again!

You can't put Gatsby inside something like react-boilerplate. Gatsby wants to handle the build and running the site. Instead, put "app" parts inside Gatsby.

yeah, that's what I meant... gatsby handles all my website and it's pages, and react-boilerplate goes inside gatsby under /app as a client-only-route... is that possible (considering its stack, specially the react-router-redux) ?

Gatsby handles all the webpack/Babel/other config already so the bootstrap project isn't needed.

@KyleAMathews I figure out how to get my client-only app with redux + apollo client working.. thank you very much for replies 😉 👍

@KyleAMathews I'm facing a small issue here, I'm using graphql apollo client inside my client only pages, but they are just for client-side (once you need to be logged in), but Gatsby tries to generate the index file on build; what, of course, gives an error

any suggestion on how to skip this HTML creation?

update:
I'm using https://www.gatsbyjs.org/packages/gatsby-plugin-create-client-paths/

I created a plugin that deletePage based on page.path, it's working flawlessly... not sure if it's the best way, but is working for my use case 😄 (yeah, now I need to create a redirect rule on nginx to always send to my app/index.html, but that's exactly what I did with my previous app...

I'm getting more and more used to gatsby, and need to confess I'm happier every progress I do... awesome work @KyleAMathews !! 👏

@KyleAMathews I'm sorry to bother you here but it seems like a good place to ask about client side routing since I haven't been able to wrap my head around it.

So for my use case I'm reading data from firebase but this data is not ALL available at build time, since users can modify it.

So in a Gatsby Page (ex: /podcasts) I can easily query the data from firebase in cDM. But then I'd like to go to the details page (ex: /podcast/:id) and there's where I get a bit lost. Should I try to delegate that route to client side routing?

As I understand the idea for the /app scape hatch is to have a place where you can just have an SPA under there, but it seems like overkill for what I'm trying to do.

Thank you for your work in gatsby, it has been a great experience all around :)

@gafemoyano creating a route for /podcast/:id totally makes sense if there'll be podcasts created by users as they're working on things. One downside to that is it slows TTFP for people visiting podcast pages directly as now there's server rendered HTML to load for them. You could perhaps do a hybrid too — statically render podcast pages that exist at build and then create more on the fly in the browser as people make them.

Of interest to those on this page — I wrote up this new docs page on building apps with Gatsby https://www.gatsbyjs.org/docs/building-apps-with-gatsby/

hey @KyleAMathews as this have become the official "client side issue" hahah another thing I've been considering on this podcast issue, considering we have control on both podcasts frontend and backend, is there any way to trigger the rebuild of specific page only? or just cache the build and change just what have been modified. I'm not sure how this could works

possible related to https://github.com/gatsbyjs/gatsby/issues/3444

you commented about a key/value cache api to store on https://github.com/gatsbyjs/gatsby/issues/3260#issuecomment-352856214, maybe if we have the layout + page content to ensure nothing has changes (on data and visual)

@KyleAMathews Thank your for your reply! So let me see if I understand correctly. The approach is to delegate a path to the client code to render, right? So I shouldn't try to define my routes statically in gatsby-node.js like:

` // page.matchPath is a special key that's used for matching pages
  // only on the client.
  if (page.path.match(/^\/podcasts/:id/)) {
    page.matchPath = "/podcasts/:id";

    // Update the page.
    createPage(page);
  }

But rather just use what's shown in the example:

```
// page.matchPath is a special key that's used for matching pages
// only on the client.
if (page.path.match(/^/app/)) {
page.matchPath = "/app/:path";

// Update the page.
createPage(page);

}


And on app/index.js I would define my routes by importing from ReactRouter directly:

import { Switch, Route } from 'react-router-dom'
const AppIndex= () => (







)
```

Which would allow me to visit /app/podcasts/:id and render PodcastDetails where could access the :id part of the path to fetch the data in the component?

Sorry for troubling you with such a simple scenario, I just wasn't able to figure it out with the existing examples. Maybe we should include an example for hybrid apps if it's a fairly common thing people do with gatsby? I'd be willing to help with that if it's needed.

Thanks again for your time building and supporting this library @KyleAMathews .

The app part of the path in the example is arbitrary. You can use whatever name you need e.g. podcasts.

An example site would be great :-) hopefully will have time soon. Invite anyone else following along who's solved this problem already to share some sample code!

I have tried and have some sample code here

But I still have some issues.
One I described here
In short, when I build for production and enter to a route under the /app/ directory, e.g localhost:9000/app/posts/1 and refresh the browser, I get a 404 blank page.
When I refresh the page in localhost:9000/app/ it works fine.
Maybe my prefixes configuration for the gatsby-plugin-create-client-paths are wrong.

module.exports = {
  ...
  plugins     : [
    {
      resolve: `gatsby-plugin-create-client-paths`,
      options: {prefixes: [`/app/*`]},
    },
    ...
};

And another issue is (not sure it's an issue) I cannot wrap my <Route />'s with <BrowserRouter>.
When I build for production (development works fine) I get an error message saying "browser history needs a DOM", I believe it's because Gatsby runs in a Node environment and it has no browser thus has no window etc.

Finally I removed the <BrowserRouter> wrap and it works fine.
I am new to React so I'm not sure it's the appropriate solution to the problem.

Would love to get some help :)

@danielemesh Hi Daniel. I haven't had time to get back working on my gatsby app, but what I can see from your source code is that you've placed the /app/* directory within /pages.
I'm not sure this where it's supposed to go, I'd try putting it on the src/ directory.

Let me know if it works!

Cheers!

@gafemoyano tried it, didn't work :(
Gatsby won't recognise it..

Thanks!

I've faced some plugins so decided to write mine (100% borrowed from original one), so I could, luckily, solve my problem and learn how to write gatsby plugins

I extracted from an application, hope it helps to solve your problems, the problem was faced bc inside app has graphql queries that should not be handled on SSR, just browser

@KyleAMathews what you mean by an example site? wanna add somewhere? I can work on this..

gatsby-config.js

plugins: [
    `app-layout`, // I set my layout
    {
      resolve: `app-client-only`, // I handle app pages
      options: { prefixes: [`/app/*`] },
    },
  ],

plugins/app-layout/gatsby-node.js

// Implement the Gatsby API “onCreatePage”. This is
// called after every page is created.
exports.onCreatePage = ({ page, boundActionCreators }) => {
  const { createPage } = boundActionCreators;

  if (page.path.match(/^\/app/)) {
    // It's assumed that `app.js` exists in the `src/layouts/` directory
    page.layout = "app";
  }

  return true;
};

plugins/app-client-only/gatsby-node.js

// Prefixes should be globs (i.e. of the form "/*" or "/foo/*")
const validatePrefixEntry = prefix => {
  if (!prefix.match(/^\//) || !prefix.match(/\/\*$/)) {
    throw Error(
      `Plugin "gatsby-plugin-client-only-paths" found invalid prefix pattern: ${
        prefix
      }`
    )
  }
}

exports.onCreatePage = ({ page, store, boundActionCreators }, { prefixes }) => {
  const { createPage, deletePage } = boundActionCreators
  const re = {}
  prefixes.forEach(validatePrefixEntry)

  return new Promise(resolve => {
    // Don't set matchPath again if it's already been set.
    if (page.matchPath || page.path.match(/dev-404-page/)) {
      resolve()
    }

    prefixes.some(prefix => {
      if (!re[prefix]) {
        // Remove the * from the prefix and memoize
        const trimmedPrefix = prefix.replace(/\*$/, ``)
        re[prefix] = new RegExp(`^${trimmedPrefix}`)
      }

      // Ensure that the path ends in a trailing slash, since it can be removed.
      const path = page.path.match(/\/$/) ? page.path : `${page.path}/`

      if (path.match(re[prefix])) {
        page.matchPath = prefix.replace(/\*$/, `:path`)
        if (path != '/app/') {
          // <<<<<<<<<<<<<<<<< here is my modification >>>>>>>>>>>>>>>>>>>>>>>
          // do not try to process on SSR, user needs to be logged to
          // consume GraphQL API and render `app` pages correctly
          deletePage(page)
          // <<<<<<<<<<<<<<<<< here is my modification >>>>>>>>>>>>>>>>>>>>>>>
        }
        // createPage(page)
        return true
      }

      return false
    })

    return resolve()
  })
}

So i'm not sure if this issue is 100% related @KyleAMathews, but no matter what I do, my client-only-path 404s initially, then it starts loading (and users leave before it starts loading)

pages/app/index.js:

import CreateSchedule from './components/CreateSchedule'
import ViewSchedule from './components/ViewSchedule'
...
  <ApolloProvider client={client}>
        <Provider store={store}>
          <Switch>
            <Route exact path="/app" component={CreateSchedule} />
            <Route path="/app/:id" component={ViewSchedule} />
          </Switch>
        </Provider>
      </ApolloProvider>

gatsby-node.js

exports.onCreatePage = async ({ page, boundActionCreators }) => {
  const { createPage } = boundActionCreators

  // page.matchPath is a special key that's used for matching pages
  // only on the client.
  if (page.path.match(/^\/app/)) {
    page.matchPath = '/app/:path'

    // Update the page.
    createPage(page)
  }
}

I've also tried the gatsby-plugin-create-client-paths plugin without luck.

My CreateSchedule component works fine without 404ing: https://www.appointmentscheduler.org/app

Issue is in the ViewSchedule Route/component : https://www.appointmentscheduler.org/app/1b42d8e8-66b5-4a8d-a0b5-fd4bb13bed09

Oh and the 404 only occurs once built - dev server doesn't have this issue

Any ideas?

@rozenmd You need server routing for that. If you use netlify you can install gatsby-plugin-netlify and it will generate server routing config for you automatically (I see you have netlify-identity-widget - not sure if that exactly mean you are using that for serving your site)

Awesome!
Thanks @pieh!
Looks like the netlify starter I used (https://github.com/konsumer/gatsby-starter-bootstrap-netlify) didn't have 'gatsby-plugin-netlify' in gatsby-config.js

Adding that and deploying has fixed this issue 😄

@KyleAMathews one additional issue potentially with using Gatsby it seems is the likelihood of 503 (denial of service) responses from the servers where the API is hosted, due to the nature of Gatsby's sucking-up-the-whole-api-at-once approach. I am currently experiencing this with GoDaddy hosting; when I run 'gatsby develop', it seems the maximum concurrent connection limit is instantly reached. Is this in fact was is going on? It works great locally (decoupled Drupal > Gatsby), but not when hosted at GoDaddy. Any tips very much appreciated.

@cf73 have you tried pointing your GoDaddy DNS to something more apt for Gatsby like Netlify?

@rozenmd to clarify, the drupal headless CMS is hosted on GoDaddy; the Gatsby site is still running locally. so unless I've misunderstood you, i don't see how Netlify could help?

@cf73 https://github.com/gatsbyjs/gatsby/blob/master/packages/gatsby-source-drupal/src/gatsby-node.js probably could use some kind of queue (we are using better-queue in other places) instead of Promise.all to limit concurrent request to something more manageable. Do you feel you could implement it?

@pieh yes i saw the use of better-queue, sounds like a decent solution. afraid i'm not up to doing it myself though -- i stumbled here trying to resolve the 503 error for a big client project i'm working on, for which i'd love to use Gatsby. deadlines are tight, so if there isn't a work around for this in the next few hours or at most next day or so, i'm going to have to look for another approach. can anyone suggest what i could do instantly (including switching hosting if need be) to resolve this? is there a proven drupal + hosting + Gatsby workflow?

@cf73 I feel you about deadlines - if you could share config for your drupal site, so I would have site to test changes against (either publicly here or privately on discord - https://discordapp.com/invite/0ZcbPKXt5bVoxkfV with PM to me - my handle there is grajen3), I would see if I can do it myself today

@pieh that would be amazing, thanks!!

@KyleAMathews i'm facing a desperate issue while working on a client site, which i'm sure is easy but I'm missing something. Stack is Drupal JSON-API to Gatsby's graphiql. It does not allow me to pass arguments to nodes (see attached). As far as I can tell, this is because Gatsby's Drupal schema is not fully fleshed out? Or am I missing a step? Any help urgently and MUCH appreciated!!
unknown-arg

The query should be:

NodeArticle(id: { eq: GUID }) {
  id
  ...otherFields
}

You could also filter allNodeArticle by the id but if you're only selecting one thing, it's cleaner to query NodeArticle directly.

@KyleAMathews thank you so much! Can you point me to any documentation where this is covered? I have not come across this so far... is it unique to how Gatsby talks with Drupal, or a core standard behavior of GraphQL that I've just missed? Might be an idea to promote any source-specific documentation such as this more visibly alongside the source-plugin?

This is gatsby core feature (filtering done root query level), not drupal specific. Source plugins can't define graphql schema - this is task that gatsby core is doing based on "raw" data provided by plugins.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

totsteps picture totsteps  ·  3Comments

magicly picture magicly  ·  3Comments

mikestopcontinues picture mikestopcontinues  ·  3Comments

ghost picture ghost  ·  3Comments

brandonmp picture brandonmp  ·  3Comments