# deno-graphql

This repository is a proof of concept that shows how to create a [GraphQL Yoga](https://the-guild.dev/graphql/yoga-server) service which runs on top of the [Deno](https://deno.com/runtime) runtime.


## Quickstart

```shell
brew install deno
echo 'export PATH="$HOME/.deno/bin:$PATH"' >> ~/.zshrc
deno install -qAf --unstable https://deno.land/x/denon/denon.ts
brew install pre-commit
pre-commit install
denon start
```

Then navigate to
[`http://localhost:8080/graphql`](http://localhost:8080/graphql).

## Deno

### Dependencies

Deno manages dependencies very differently from Node. Dependencies come from CDN URLs in a JSON file, called the "import map" file (example [here](./deno.json)). The import map does two things:

- Sets the dependency namespaces for imported libraries (the keys in the map), used in `import` statements
- Specifies the location of of the dependencies (the values in the map)

For Deno-core libraries like, say, the http server, they will typically come from the `deno.land` CDN. There's also backwards compatibility for pulling packages from `npm`, which you will see with the `npm` prefix in the import map.

You have to teach your IDE how to handle the import map. We'll cover how it works for VSCode later.

### Imports

Remember how we said the keys in the import map were the namespaces for the imports? That's important when you want to import something. Say you have this import map:

```json
{
  "imports": {
    "graphql": "npm:graphql@^16.6.0"
  }
}
```

You import this in your Typescript files using:

```typescript
import graphql from "graphql";
```

Because this is configurable, you can do weird stuff like:

```json
{
  "imports": {
    "foobar": "npm:graphql@^16.6.0"
  }
}
```

```typescript
import graphql from "foobar";
```

### System Permissions

One of the more interesting things about Deno as a runtime is the way that it manages permissions via its [Permissions API](https://deno.land/manual@v1.32.4/runtime/permission_apis). Any
time you want to request permissions for underlying operating system resources (`net` for using network, `read` for using disk read, etc.) you have to add flags to the `deno` command:

```shell
deno run --allow-net --allow-read
```

If you don't do this, the Deno runtime will interrupt and ask you to accept permissions for the flags you've left out:

```shell
 ⚠️  Deno requests net access to "0.0.0.0:8000".
├ Requested by `Deno.listen()` API.
├ Run again with --allow-net to bypass this prompt.
└ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all net permissions) >
```

It's a bit annoying to have to write out all your permissions as CLI arguments, especially if you're writing something that is likely to use a lot of them. Denon solves this (among other things), which we'll get to in that section.

### Linting and Formatting

You've got two commands:

```shell
deno lint
deno fmt
```

You can customize this, but the docs discourage it.

### Installation

```shell
brew install deno
echo 'export PATH="$HOME/.deno/bin:$PATH"' >> ~/.zshrc
```

## Denon

[Denon](https://github.com/denosaurs/denon) functions as a sort of spiritual successor to `npm` or `yarn`, in that it manages scripts (however, it does _not_ manage dependencies; import maps do that). It also watches for filesystem changes to source and pulls in changes like `nodemon`. Denon manages your permissions in any script's `allow` subfield. See
[`scripts.json`](./scripts.json) for how it's configured in this project. There's also a way this can be Typescript instead of JSON which could be useful.

So Denon is good for:

- Managing scripts (like `npm` or `yarn`)
- Watching changes in the filesystem. It does the same thing as `nodemon` (which
  should be disabled when running in a Docker container using the
  `"watch": false` script configuration argument)
- Managing script-level permissions (which don't exist in `node`)

The Denon scripts file looks like:

```json
{
  "$schema": "https://deno.land/x/denon@2.5.0/schema.json",
  "scripts": {
    "start": {
      "cmd": "deno run src/app.ts",
      "desc": "run my src/app.ts file",
      "importMap": "deno.json",
      "allow": [
        "net",
        "read",
        "sys"
      ]
    }
  }
}
```

Specifying that when we `denon start`, the `start` process is permitted to use `net` `read` and `sys` permissions as well as the location of the `importMap` (this field used to be called `importmap`, so some of the resources you'll find for it online will be incorrect, be aware of it and don't let it slow you down).

### Installation

```shell
deno install -qAf --unstable https://deno.land/x/denon/denon.ts

denon start
```

## VSCode

Install the Deno VSCode plugin. Initialize it using
`> Deno Initialize Environment`

For some reason VSCode is frequently falling out of date with the import map and giving me all errors my Deno imports aren't properly defined. It's wrong, but I don't seem to be able to tell it to refresh.

## Conventions

Use `mod.ts` for package exports.
[Do not use `index.ts`](https://deno.land/manual@v1.32.4/references/contributing/style_guide#do-not-use-the-filename-indextsindexjs).
