You may have noticed when running an application you need to provide explicit permissions for everything that runs. This can be cumbersome and we want development to be easy (at least easier, right?). Lets learn a better way!
Setup
If you haven't already, you'll need to install deno. For instructions, check out my getting started guide.
Now that you have deno installed, we will create a simple project and learn how to output environmental variables and look into what we can do to make the permissions part easier.
In your project directory, create a new file called index.ts
.
Let's create a simple line of code in it: console.log('Hello World');
Adding some functionality
That's simple enough but wouldn't it be cool if we could have it spit out a "Hello [username]"? (Your username from terminal)
From terminal you can use whoami
or echo $USER
and you will see the output is your username. (Works on Mac only).
We can use a built in Deno.env
feature that has a get
, a set
, and a toObject()
function. We are going to use the .get
which will retrieve the value of an environmental variable (Returns undefined if that key doesn't exist).
We want to get our USER variable - that will look like this:
console.log(`hello ${Deno.env.get('USER')}`); //note the backticks and the template literals
What happens if we run this?
In terminal run deno run index.ts
You will get an error:
$ deno run index.ts error: Uncaught PermissionDenied: access to environment variables, run again with the --allow-env flag at unwrapResponse ($deno$/ops/dispatch_json.ts:42:11) at Object.sendSync ($deno$/ops/dispatch_json.ts:69:10) at Object.getEnv as get at file:///Users/drew/Development/index.ts:1:31
Now this is nothing new, we kind of went over this in the security section of my future of the backend development with javascript article.
So we can just give it permissions right?
This is a good thing though because our programs shouldn't be able to out of the box reach into our environment and pull (potentially sensitive) data without our permissions.
To avoid the error we'd need to run it as deno run --allow-env
.
What if we don't want to be bothered with --allow-env
every time we want to run our project? We have some alternatives!
You could use deno run --allow-all index.ts
and that will run or we can do the shorter version deno run -A index.ts
. Now this will allow anything to run without specific permissions - not necessarily a good thing because it is removing some potential warnings you'd likely want to know about if you downloaded this code from a random imported URL on the internet.
This is a key benefit of deno so disabling it isn't recommended. We really want to give it the least amount of privilege necessary to perform it's intended task and we only want to allow access to the specific services that are necessary - no more (sounds redundant but isn't).
We don't want network access or the ability to load plugins and the like.
There are some approaches we can take to make our permissions a bit easier.
Setting permissions
Let's assume we have done a lot more work on this project and we are ready to make this thing go live and easy to use.
We could create some scripts in a bash shell that will run that program, but you'd need several versions for caching, running, testing, and for different operating systems.
Deno has a way to do this built in. All you need to use is deno install --allow-env index.ts
. Deno will compile our file with permissions.
If we cat
that file, you can see it created a bash script like below:
Now we can open that file directly and will have access to it without providing any specific permissions or having to manually create a bunch of bash scripts!
That's cool but how is writing out the full path better?
Use deno help install
and you will see if you scroll up some options to change the command's name from the directory it's in.
To change the executable name, use -n/--name: ex. deno install --allow-net --allow-read -n exampleName
We will want to run deno install --allow-env -n hello index.ts
. Bear in mind that all the flags have to go before the target file (index.ts) - order matters.
We will then need to export the path provided as you can see in the gif below.
After doing that, hello
will be a valid command in terminal and we won't even need to provide it the permissions!
This is useful for a production option but what about development? For that, we will need something more like a task runner. More on that in the next section.
Development (Task Runner)
Task runners will be more and more useful as we develop heavier and heavier programs that require different configurations and permissions. This is a role that is filled by npm and packages.json in node. This is similar to "Make", "Ant", "Rake", & "MSBuild".
Deno has a third party module called "drake" which is a make like task runner for deno.
Navigate in your browser to https://deno.land/x/drake/README.md
If you copy the example into a new file we will call Drake.ts
.
In the import statement within the curly brackets add , sh
. Now this will allow us to run commands from your shell script.
Within the task function we want to use a sh()
function and pass in our deno run ---allow-env index.ts
shell command and you will need to make this asyncronous. It should look something like this:
import { desc, run, task, sh } from "https://deno.land/x/[email protected]/mod.ts"; desc("Minimal Drake task"); task("hello", [], async function() { console.log("Hello From Drake!"); //Make this say what ever you like! await sh('deno run --allow-env index.ts'); }); run()
Now we want to run drake with "all" permissions and what script to run (in this case hello
) because drake is already passing in all the permissions in line 6 that we need!
Run deno run -A Drake.ts hello
Drake has a full API for scripting your tasks, you'll want to dig into https://deno.land/x/drake/README.md. These scripts can do anything deno can do and with the correct permissions!
Next...
In my next article we will look at Third Party Modules and Deno Tools.
If you found this article helpful, give me a shout on twitter @codingwithdrewk or if you find any errors, feel free to highlight them and mash that R button the right side of the screen.
Drew is a seasoned DevOps Engineer with a rich background that spans multiple industries and technologies. With foundational training as a Nuclear Engineer in the US Navy, Drew brings a meticulous approach to operational efficiency and reliability. His expertise lies in cloud migration strategies, CI/CD automation, and Kubernetes orchestration. Known for a keen focus on facts and correctness, Drew is proficient in a range of programming languages including Bash and JavaScript. His diverse experiences, from serving in the military to working in the corporate world, have equipped him with a comprehensive worldview and a knack for creative problem-solving. Drew advocates for streamlined, fact-based approaches in both code and business, making him a reliable authority in the tech industry.