Swizec Teller - a geek with a hatswizec.com

    Configuring your JAMStack app for prod vs. dev

    You should always keep development and production separate my friend, but how do you pull that off when everyone has your back and tries to keep your app secure?

    wait what 🀨

    Here's the problem πŸ‘‰

    You've built a cool app. Static JAM on the frontend, Serverless functions on the backend, nearly free to host, super fast to load. It's amazing and you're proud as heck.

    Time to show off!

    show_off giphy

    You know dev and production should be separate. Wouldn't want users to see a bug next time you mess around with code. Sure as heck don't want to mix test and production data.

    Prod vs. Dev on your serverless backend

    Separating development and production on your backend is easy as pie. Change the stage: in serverless.yml and voila.

    Using the demo app from ServerlessReact.Dev for example:

    Click through for source
    Click through for source

    This configures a /graphql endpoint served by an AWS Lambda and a DynamoDB table to store your data. Both the DynamoDB table and the GraphQL endpoint use stage as part of their name.

    Change that to stage: dev to stage: prod, run yarn deploy, and you get a whole new copy of your backend infrastructure. A new AWS Lambda, new DynamoDB table, everything.

    perfect giphy

    Making your stage easy to change

    Changing serverless.yml on deploy is tedious. You'll commit the wrong version to git, too.

    Define your stage with command line arguments instead!

    Like this:

    Click through for source
    Click through for source

    Now you can add a helpful little bash script – because I couldn't figure out how to do this with Node and Bash is just fine. πŸ˜‡

    Click through for source
    Click through for source

    Add to your package.json ...

    Click through for source
    Click through for source

    And you can run yarn deploy prod to deploy to production, or yarn deploy to get development by default.

    Wonderful.

    Prod vs. Dev on your JAMStack frontend

    Separating production and development on the JAM is easy in theory πŸ‘‰ push to a branch and you get a deploy preview. Press a button or push to a special branch and you get production.

    Both Netlify and Zeit do that. I'm sure others do, too.

    That was easy.

    Now, how do you tell your app that production data lives on ...amazonaws.com/prod/graphql and development should talk to ...amazonaws.com/dev/graphql?

    think_hard giphy

    That's where it gets tricky.

    I've found 3 approaches with varying levels of success and different tradeoffs.

    1. The Gatsby way

    Gatsby comes with built-in support for .env files. Put your environment variables in .env.development or .env.production, access through process.env and let Gatsby handle the rest.

    Say you want to tell Gatsby which GraphQL endpoint to use when building your JAMStack app.

    Go in gatsby-config.js and add your server as a source

    Click through for source
    Click through for source

    You can now configure the GATSBY_GRAPHQL_URL environment variable in 2 files to differentiate production and development.

    Click through for source
    Click through for source

    Click through for source
    Click through for source

    Gatsby will choose production when you run gatsby build and development when you run gatsby development.

    You can even use these in the browser!

    Click through for source
    Click through for source

    OAwReM3

    πŸ‘Œ

    The tradeoff

    The Gatsby way breaks down when you deploy.

    FJlMtA7

    Both Zeit and Netlify helpfully strip your .env files during deploy. Wouldn't want to store secrets in plaintext on their servers.

    This is great from a security perspective.

    And it breaks Gatsby support for .env files. They aren't available during build. πŸ€¦β€β™‚οΈ

    2. The Zeit way

    The Zeit way is more secure. You store secrets and configs in their secure storage. Encrypted, easy to rotate, not part of your code.

    Now you gotta jump a couple more hoops.

    First you add your secret to Zeit

    Click through for source
    Click through for source

    Then you create a now.json file

    Click through for source
    Click through for source

    And then, you use it in gatsby-config

    Click through for source
    Click through for source

    When you deploy to Zeit, the build process takes your now.json, replaces @variable with values from storage, loads the configs into process.env, and Gatsby can use them while building.

    Sounds complicated ... 🀨

    But it works. Sort of

    Netlify has a similar approach, by the way.

    The tradeoff

    Notice there's no prod vs. dev. You get a config and that's it. Zeit says they'll have native support for environments soon.

    Click through for source
    Click through for source

    The other tradeoff is that these configs aren't available at runtime. You get them when compiling and can't use them in the browser.

    And you can't import 'now.json' either. Zeit deletes that file ☹️

    3. The way that actually works

    In the end, here's what works best:

    1. Put your configs in JSON files
    2. Import in gatsby-config.js
    3. Add to siteMetadata
    4. Access via static queries

    Kinda bonkers but it works.

    You start with 2 JSON files similar to the original .env approach.

    Click through for source
    Click through for source

    Click through for source
    Click through for source

    Import in gatsby-config.js

    Click through for source
    Click through for source

    Use the configs directly in your build-time code via config.X. Works just fine.

    You can import JSON files in the browser, sure, but you won't know which environment. Put them in siteMetadata.myConfig instead, so you can get configs with static queries anywhere in your app.

    Like this

    Click through for source
    Click through for source

    And you get ...

    ujVZMF7

    yay giphy

    The tradeoff

    You're storing configs in version control. In plaintext. They better not be secret πŸ˜‰

    And you'll have to re-deploy any time you change a value.

    But hey at least it works.

    In conclusion

    There's no perfect solution here yet, I'm afraid. The JSON approach combined with static queries seems best for now. If you've found something better, I'd love to know.

    Happy hacking ✌️

    Cheers,

    ~Swizec

    PS: I like to use AWS SecretsManager on the backend for real secrets, maybe there's an API you could use from the browser πŸ€”

    Did you enjoy this article?

    Published on March 26th, 2020 in Back End, Front End, Technical

    Learned something new?
    Want to become an expert?

    Here's how it works πŸ‘‡

    Leave your email and I'll send you thoughtfully written emails every week about React, JavaScript, and your career. Lessons learned over 20 years in the industry working with companies ranging from tiny startups to Fortune5 behemoths.

    Join Swizec's Newsletter

    And get thoughtful letters πŸ’Œ on mindsets, tactics, and technical skills for your career. Real lessons from building production software. No bullshit.

    "Man, love your simple writing! Yours is the only newsletter I open and only blog that I give a fuck to read & scroll till the end. And wow always take away lessons with me. Inspiring! And very relatable. πŸ‘Œ"

    ~ Ashish Kumar

    Join over 10,000 engineers just like you already improving their careers with my letters, workshops, courses, and talks. ✌️

    Have a burning question that you think I can answer?Β I don't have all of the answers, but I have some! Hit me up on twitter or book a 30min ama for in-depth help.

    Ready to Stop copy pasting D3 examples and create data visualizations of your own? Β Learn how to build scalable dataviz components your whole team can understand with React for Data Visualization

    Curious about Serverless and the modern backend? Check out Serverless Handbook, modern backend for the frontend engineer.

    Ready to learn how it all fits together and build a modern webapp from scratch? Learn how to launch a webapp and make your first πŸ’° on the side with ServerlessReact.Dev

    Want to brush up on your modern JavaScript syntax?Β Check out my interactive cheatsheet: es6cheatsheet.com

    By the way, just in case no one has told you it yet today: I love and appreciate you for who you are ❀️

    Created bySwizecwith ❀️