Second adventure in deno land

Lock files

To complete the information from the last post about dependencies, I’ll write about lock files. They’re a standard practice in many languages in pretty much every production app. They’re used to describe an exact tree of dependencies, in order to make installations more repeatable, avoiding issues that may arise out of version misalignement.

deno cache --lock=lock.json --lock-write ./src/deps
$ deno cache -r --lock=lock.json deps.ts

Official VSCode extension

The official vscode extension has been launched! However, it is the exact same that I have mentioned on my previous post. It simply got moved to the official repo, as the changelog states

Documentation

Another of the advantages presented by Ryan in his talk was that deno included a documentation generator on its toolchain. It doesn’t have (yet) a section on the website, but we’ll explore it a bit here.

Browse modules documentation

Even though deno has no install step, the cache lets you develop in an airplane (as you did with node_modules), as it loads the modules the first time and then uses the cached one.

$ deno doc https://deno.land/std/http/server.ts
function listenAndServe(addr: string | HTTPOptions, handler: (req: ServerRequest) => void): Promise<void>
Start an HTTP server with given options and request handler
function listenAndServeTLS(options: HTTPSOptions, handler: (req: ServerRequest) => void): Promise<void>
Start an HTTPS server with given options and request handler
function serve(addr: string | HTTPOptions): Server
Create a HTTP server
function serveTLS(options: HTTPSOptions): Server
Create an HTTPS server with given options
class Server implements AsyncIterableclass ServerRequestinterface Response
Interface of HTTP server response. If body is a Reader, response would be chunked. If body is a string, it would be UTF-8 encoded by default.
type HTTPOptions
Options for creating an HTTP server.
type HTTPSOptions
Options for creating an HTTPS server.
$ deno doc https://deno.land/std/http/server.ts listenAndServe
function listenAndServe(addr: string | HTTPOptions, handler: (req: ServerRequest) => void): Promise<void>
Start an HTTP server with given options and request handler
const body = "Hello World\n"; const options = { port: 8000 }; listenAndServe(options, (req) => { req.respond({ body }); });
@param options Server configuration @param handler Request handler
$ deno doc twitter/client.ts
const search
Searches for the recent tweets of the provided username that have more than 5 likes
interface Tweet
Fields in a tweet
interface TweetResponse
The response from Twitter API

Fine-grained permissions

As we talked in my previous post, one thing that deno got very well was permissions. They’re easy to use and secure by default. Previously, I’ve explained that in order for a script to be able to access the network, for instance, you’d have to explicitly use --allow-net flag when running it.

deno run --allow-net=api.twitter.com,0.0.0.0 index.ts

Running code in the browser

Another very interesting feature of deno, is the bundle command.

import { TweetResponse } from "../twitter/client.ts"export function popular(handle: string): Promise<TweetResponse> {
return fetch(`http://localhost:8080/popular/${handle}`)
.then(res => res.json())
.catch(console.error)
}
$ deno bundle client/index.ts public/client.js
<script type="module">
// Imports the generated client
import * as client from "./client.js"
async function fetchTwitter(event) {
// Uses the methods on it
const result = await client.popular(event.target.value)
/*
Omitted for brevity
*/
}
</script>
# inside the public folder
$ deno run --allow-net --allow-read https://deno.land/std/http/file_server.ts

Testing

Following deno’s goal of shipping the essentials in a single binary, testing is obviously included. The documentation is, again, quite good on this. With the help of the doc command, it gets very straight-forward to write tests.

$ deno doc https://deno.land/std/testing/asserts.ts
function assert(expr: unknown, msg): <UNIMPLEMENTED>
Make an assertion, if not `true`, then throw.
function assertEquals(actual: unknown, expected: unknown, msg?: string): void
Make an assertion that `actual` and `expected` are equal, deeply. If not deeply equal, then throw.
function assertMatch(actual: string, expected: RegExp, msg?: string): void
Make an assertion that `actual` match RegExp `expected`. If not then thrown
/* Cut for brevity */function fail(msg?: string): void
Forcefully throws a failed assertion
import * as ApiClient from "./index.ts"
import { assertEquals } from "../deps.ts"
import { runServer } from "../util.ts"
Deno.test("calls the correct url", async () => {
runServer(async req => {
assertEquals(req.url, "/popular/ampsantos0")
await req.respond({ body: JSON.stringify({ statuses: [] }) })
})
await ApiClient.popular("ampsantos0")
})
import { serve, ServerRequest } from "./deps.ts"export const runServer = async (
handler: (req: ServerRequest) => Promise<any>
) => {
const server = serve(":8080")
for await (const req of server) {
await handler(req)
server.close()
}
}
$ deno test --allow-net
test calls the correct url ... error: Uncaught AssertionError: Values are not equal:    [Diff] Actual / Expected
- "/popular/ampsantos0"
+ "/popular/handle-that-doesnt-work"
throw new AssertionError(message);
^
at assertEquals (https://deno.land/std/testing/asserts.ts:170:9)
at file:///Users/alexandre/dev/personal/deno/testing-deno/client/index.test.ts:7:5
at runServer (file:///Users/alexandre/dev/personal/deno/testing-deno/util.ts:9:11)
$ deno test --allow-net --filter="correct url"
$ deno test --allow-net ./client

Conclusion

On our second adventure, we went a little further than just presenting the language.

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store