serverless-next.js
serverless-next.js copied to clipboard
Use environment variables
How can I pass environment variables? I would like to pass some api key and secrets both to api and frontend lambdas, how can I do that?
I tried like this but it doesn't work:
# serverless.yml
myApp:
component: serverless-next.js
inputs:
env:
API_KEY: demo
@macs91 Use next's build time configuration . This is the recommended approach for managing environment config. in your pages.
@danielcondemarin I tried it, but I see that it leaks env variables to the client... Given that I intend to store secrets I can't use that 😬
@danielcondemarin I tried it, but I see that it leaks env variables to the client... Given that I intend to store secrets I can't use that 😬
Ohh I see. Sorry I wasn't entirely clear how build time env. works. Sounds like there is scope then for injecting env. variables via serverless.yaml. Reopening ...
@macs91 Could you share how you're accessing the env. config. in the page? i.e. on getInitialProps I'm guessing?
Yes, the secret will be accessed from getInitialProps.. I dug a bit more, and it seems that only the env variables that are explicitly referenced on client side code are visible in the bundle, so basically env variables are not exposed unless they are explicitly included in client side code?
So If I don't want the env variable to get included in the bundle maybe it's enough to use it under this condition:
if (!process.browser) {
console.log("my secret", process.env.SECRET);
}
Ok so maybe this way it can work, but we have to be careful to make sure the env variable are not referenced inside the client bundle.
@macs91 Sounds incredibly easy to leak the secure config. by accident., so I'll be adding support for runtime only env. configuration via a serverless.yml input. Stay tuned 😄
Yep I thought that too 😅 When env variables are set only on the lambda it's much harder to leak secrets by mistake. Thanks 👍
@macs91 Found out today Lambda@Edge doesn't support environment variables :/
Hopefully this will be supported / available soon! But for now you'll have to workaround it by using the build time config. with care. I'll keep the issue opened.
Bummer! I didn't know that.. Hopefully they will announce support at re:Invent 😁
Bummer! I didn't know that.. Hopefully they will announce support at re:Invent 😁
That's what I'm hoping. All I can do for now is ask for it 🙂
Oh no... is there still no way to do this? I used NextJS and this component to handle a simple contact form but I'd like to not leak the email address the form sends the submission to.... Anyway to handle this? If not then this has practically killed my app with this dead in the water :(
@gbwashleybrown Did you have a look at build.env ? https://github.com/danielcondemarin/serverless-next.js#inputs
It uses next's build time env variables as documented in https://nextjs.org/docs/api-reference/next.config.js/environment-variables
@danielcondemarin According to the conversation further up this post, doing that will leak values into the client? So I would prefer not to do that as I don't need to access them in the client.
My serverless function loops trough an array of email addresses and picks one to send the contact form submission depending on some stuff. So being able to access all the email addresses on the server side only without exposing them to the client is key for me to be able to use this in a production environment.😕
@gbwashleybrown The safest way to do this is to create a handler in a /api route then POST/GET to that handler to loop your array etc, /api routes are not exposed to the client as they are a simple request/response and always run on the server side, you may find using this that you don't need process.env vars at all,
Take a look at https://nextjs.org/docs/api-routes/introduction
And also a rest example here: https://github.com/zeit/next.js/tree/canary/examples/api-routes-rest
@Podders Thank you.
I think I may have misunderstood how Next is bundled.
The email array and sendmail method is within /pages/api/sendmail.js and I was under the impression that /api/* is exposed to the client.
For as long as server-side functions (/pages/api/*) files aren't exposed then I should be ok. 😁
I found this issue when I had a lambda function (created with '@serverless/backend') that would be deployed along side my next.js app and I wanted to expose an environment variable to the next.js app during build time that was equal to the url of the newly deployed lambda function.
my solution was as follows
// serverless.yml
api:
component: '@serverless/backend'
inputs:
code:
src: api/src
env:
dbName: ${database.name}
dbRegion: ${database.region}
frontend:
component: "serverless-next.js"
inputs:
domain: "yourdomain.com"
build:
env:
API_URL: ${api.url}
// next.config.js
module.exports = {
target: "serverless",
env: {
API_URL: process.env.API_URL
}
}
// pages/index.js
export async function getStaticProps() {
const API_URL = process.env.API_URL
return {
props: {
API_URL
}
}
}
[CORRECTION] Adding my env variables in the next.config.js file suffuced:
module.exports = {
env: {
//env variables here
}
}
What we ended up doing was using AWS Secrets Manager, since you can request from Lambda@Edge. Downside is having to make a region-specific request to get the secrets from the Lambda, but works as a stop-gap for now.
@gbwashleybrown Did you have a look at
build.env? https://github.com/danielcondemarin/serverless-next.js#inputs It uses next's build time env variables as documented in https://nextjs.org/docs/api-reference/next.config.js/environment-variables
Is this working? I have been trying this method and it doesn't seem like build.env does anything at all...
Maybe I am misunderstanding how this works, so far in order to set a env variable I used:
appName:
component: '@sls-next/[email protected]'
build:
env:
NEXTAUTH_URL: https://example.cloudfront.net
Say NEXTAUTH_URL thought is not being set at all, as in for this specific example is not fetched by next-auth, what am I missing?
EDIT:
Of course the following would work:
// next.config.js
module.exports = {
env: {
NEXTAUTH_URL: https://example.cloudfront.net
}
}
But wouldn't that both expose the env variable to the bundle and also to VCS? How can we avoid that?
@MelMacaluso I had the same problem. I am also using next-auth. Look at the solution by @bobrown101. That worked for me. build.env must go under inputs. So the documentation is slightly off.
@andrew310 thanks mate! Will try out and let you know 🙏
Actually the only thing that works for me is defining the envs in the next.config.js which is not ideal as is commited...if anyone has some other solutions would be greatly appreciated 🙏
Actually I am facing issue to maintain the deployment process with the same build which I created for my DEV environment. As you know we need to deploy the same code on different environments(DEV, UAT & PROD), and have different endpoints for each environment. So I want to deploy the build which I created for DEV environment to UAT & PROD, but how can I change the end-point without making the new build of nextjs code?
Below is how I was able to include environment variables without needing to commit them to source (assumes you're using GitHub actions to deploy your app).
Tradeoffs of this approach:
- ❗❗❗ As others have mentioned, there's nothing to stop you from sending secret environment variables down to the client, so you have to be careful
- You have to remember to add all of your secrets to multiple places 😐 (GitHub secrets, GitHub workflow file, serverless yaml,
next.config.js) - This will quickly get annoying when working with different environments that need different values. In fact, you can only have 100 secrets per GitHub workflow, so this approach doesn't scale well with multiple environments
1. Store environment variables as secrets in your GitHub project's "Secrets" section
This will be at https://github.com/<username>/<project>/settings/secrets

2. Have your GitHub workflow include the variables during the build process
Assuming your GitHub workflow looks something like this one, include your desired environment variables in the step where the app is built and deployed
- name: Deploy to AWS
run: npx serverless
env:
MY_SECRET_API_KEY: ${{ secrets.MY_SECRET_API_KEY }}
3. In your serverless yml, include the variables under build.env (again assumes a structure somewhat like this one))
myApp:
component: serverless-next.js
inputs:
...
build:
env:
MY_SECRET_API_KEY: ${env.MY_SECRET_API_KEY}
4. In your next.config.js, add the variables you want included
module.exports = {
target: 'serverless',
env: {
MY_SECRET_API_KEY: process.env.MY_SECRET_API_KEY
}
};
... and that's pretty much it. This isn't a great solution because of the tradeoffs already mentioned, and if you have the time to sink into figuring out how to get AWS Secret Manager working, that would probably be a better workaround. It's unfortunate that there's no easy way to configure serverless next.js without committing your env vars to source as that's an anti-pattern.
Just a note from me. I was also struggling with the same issue and @murtyjones solution just did not work for me. The problem seems to be step 3 where I needed to change
myApp:
component: '@sls-next/[email protected]'
inputs:
...
build:
env:
MY_SECRET_API_KEY: ${env.MY_SECRET_API_KEY}
to
myApp:
component: '@sls-next/[email protected]'
inputs:
...
env:
MY_SECRET_API_KEY: ${env.MY_SECRET_API_KEY}
So I needed to remove build nesting for env in serverless.yml file.
Best regards
@murtyjones Your comment "figuring out how to get AWS Secret Manager working, that would probably be a better workaround," -- Makes me believe I am pushing into a rope trying to use AWS Secrets Manager with nextjs to handle both oAuth2 secrets and DB passwords. I think I should stop trying an concede to the anti-pattern...
@murtyjones Your comment "figuring out how to get AWS Secret Manager working, that would probably be a better workaround," -- Makes me believe I am pushing into a rope trying to use AWS Secrets Manager with nextjs to handle both oAuth2 secrets and DB passwords. I think I should stop trying an concede to the anti-pattern...
yeah, it's definitely doable but it takes some effort. i've also played around with AWS Systems Manager, which looks like it might be easier to use with AWS Lambda than the Secrets Manager, but I haven't tried that yet so not sure if there are any gotchas.
this is one of those things where someone who publishes a blog post guide will rake in the traffic 😄
Environment variables are almost always needed with the backend (secret keys specifically), so everyone is going to be facing this same problem - which I suspect is a heck of a lot people? Why can't we just make this none Lambda@Edge and just use normal Lambda instead? I would to help, if I can, but I'm not particularly great at writing things like this and don't really know the codebase too well.
With that said, I'm having to store secrets in /api/* files directly, rather than an .env file. This is fine for now, but obviously not ideal as it gets saved in VCS and as you know, it's just a really insecure way of storing your code.
The approach I've gone with for a multi-env i18n Next.js site is to set a BUILD_ENV variable which is used to determine which environment variables to load in. So my serverless.yml file looks like:
myNextApp:
component: '@sls-next/[email protected]'
build:
env:
BUILD_ENV: ${env.BUILD_ENV}
inputs:
timeout: 30
build:
postBuildCommands: ['node serverless-post-build.js', 'node sitemap.xml.js']
memory: 1024
Then in the root of the repo I've got a environments folder containing .env.localhost, .env.develop and .env.main:
These files look something like:
NEXT_PUBLIC_BUILD_ENV=develop
NEXT_PUBLIC_AUTH_DOMAIN=auth.staging.yoursite.com
NEXT_PUBLIC_SITE_URL=https://yoursite.cloudfront.net/
In my next.config.js file I include the necessaray environment variables with dotenv:
require('dotenv').config({
path: `environments/.env.${process.env.BUILD_ENV || 'localhost'}`,
});
If you've got additional post-build scripts as I have then you'll need to include the above snippet to i.e in sitemap.xml.js
Then all that is left to do is run:
BUILD_ENV=develop npx serverless