Tamia  Walter

Tamia Walter

1622702379

How to Build a Deno Plugin using Rust

This tutorial covers how to build a Deno plugin using Rust and create an async plugin using Rust futures and the deno_core crate.

Deno is a new JavaScript runtime built with Rust and V8 that enables you to run JavaScript outside the browser.  Deno is more secure than Node.js because it limits network and file system access by default.

One of the cool things about Deno is that you can write plugins in Rust and use them within Deno code. In this tutorial, we’ll show you how to create Deno plugins in Rust.

Originally published by Anshul Goyal on https://blog.logrocket.com

We’ll cover the following:

  • Why write Deno plugins in Rust?
  • Deno plugin project structure
  • Building a Rust project
  • Adding Rust code
  • Creating the optimizer function
  • Loading a Rust plugin in Deno
  • Writing async plugins

Why write Deno plugins in Rust?

Plugins in Deno generally improve performance and provide access to a wider range of tools.

Due to their performant nature, plugins are often used in calculations for heavy tasks such as image processing. Plugins also give you access to a variety of libraries written in other languages, including high-quality Rust crates.

Deno plugin project structure

The plugin project structure is the same as any Deno module. For the purpose of this tutorial, you can use this boilerplate:

git clone https://github.com/anshulrgoyal/deno-rust-starter.git my_module

First, build the Rust boilerplate for the plugin:

cd my_module/native
cargo build

Next, run a test to verify that Deno is picking up the correct library:

cd my_module/native
deno test --unstable --allow-plugin

The boilerplate includes a Rust project in the native directory and a Deno module in the root.

Building a Rust project

The Rust project compiles a dynamic library that is loaded by the Deno runtime. The file type and name of the library depend on the operating system. The Rust project may compile to a so file — dylib or dll — and the name of the compiled file may also be different. The boilerplate can handle three major platforms: Linux, macOS, and Windows.

[package]
name = "native"
version = "0.1.0"
authors = ["anshul <anshulgoel151999@gmail.com>"]
edition = "2018"

[lib]
name = "native"
crate-type = ["cdylib"]

[dependencies]
deno_core = "0.75.0"

├── README.md
├── deps.ts
├── mod.ts
├── mod_test.ts
├── native
│   ├── Cargo.lock
│   ├── Cargo.toml
│   ├── src
│       └── lib.rs
├── test.ts
├── test_deps.ts
└── tsconfig.json

The mod.ts file is the main file imported by another application using your module.

Adding Rust code

For this tutorial, we’ll show you how to build a PNG optimizer using an oxipng crate. Every Deno plugin must export the deno_plugin_init function and register all the methods that the plugin exports.

The #[no_mangle] attribute tells the compiler not to change the name of the function:

#[no_mangle]
pub fn deno_plugin_init(interface: &mut dyn Interface) {
 // register the function. Pass name and function to register method
    interface.register_op("hello_world",hello_world);
}

Creating the optimizer function

Each exported function has the same signature. Deno plugins can only export functions. These functions can be sync or async, depending on the return type.

fn optimise(_interface: &mut dyn Interface,
    zero_copy: &mut [ZeroCopyBuf],
) -> Op {
    // get first argument
    let first=zero_copy.first().unwrap();
    let opts: oxipng::Options = Default::default();
    // convert vector
    let result = oxipng::optimize_from_memory(&first.to_vec(), &opts).unwrap();
    // move to heap so that deno can use it
    Op::Sync(Box::from(result))
}

The second argument of the function contains an array of buffers. Each buffer in the array represents the argument passed to the exported function when called. These buffers are serialized to strings or other data types based on requirements.

The above code takes the first element of zero_copy and passes it to optimize_from_memory. The first element of the array is the file passed to the optimize function when called from the Deno code. The file is passed as bytes. The function processes the file and returns the result as a Box. The return type is Op enum with two variants sync and async.

Build the code using the cargo build command. Now this plugin can be used in Deno.

Loading a Rust plugin in Deno

Now that the plugin is compiled, let’s load it using Deno.

The plugin is still in development and is a part of unstable APIs, so the --unstable flag is required, as is --allow-plugin.

let path = ""
// check the type of OS to load correct file
if (Deno.build.os === "linux") {
// linux file emited by rust compiler
  path = "./native/target/debug/libnative.so"
} else if (Deno.build.os === "windows") {
// windows file emited by rust compiler
  path = "./native/target/debug/native.dll"
} else if (Deno.build.os === "darwin") {
// macos file emited by rust comipler
  path = "./native/target/debug/libnative.dylib"
}
// load plugin from file system
const rid = Deno.openPlugin(path);
// Get available methods on plugin
//@ts-Expect-Error
const { optimise:optimise_native } = (Deno as any).core.ops();

export async function optimise(fileName: string): Promise<Uint8Array> {
// reading a file
  const file = await Deno.open(fileName);
// getting content
  const value = await Deno.readAll(file)
// closing file
  await Deno.close(file.rid)
// running the native plugin method using Deno dispatch method
  return (Deno as any).core.dispatch(optimise_native, value)
}

Each plugin is loaded using the openPlugin method. Then, the ops method is used to get the method identifier, which executes the code exported by the plugin.

dispatch is used to run code exported by the native plugin. The first argument is the method identifier; the rest are passed for the native function. In this case, the file is passed.

Writing async plugins

Since Deno is single-threaded, it’s not wise to block the main thread. Deno allows you to return a future from the native function, which you can use with OS threads to write a function that doesn’t block the main thread.

fn optimise_async(_interface: &mut dyn Interface,
    zero_copy: &mut [ZeroCopyBuf],
) -> Op {
// get first argument
    let first=zero_copy.first().unwrap();
    let opts: oxipng::Options = Default::default();
    let arg=first.to_vec();
// create a new future
    let fut = async move {
// create a channel to send result once done to main thread
        let (tx, rx) = futures::channel::oneshot::channel::<oxipng::PngResult<Vec<u8>>>();
// create a new thread
        std::thread::spawn(move || {
// perform work
          let result = oxipng::optimize_from_memory(&arg, &opts);
// send result to main thread
          tx.send(result).unwrap();
        });
// receive the result
        let result=rx.await;
// create a boxed slice
        let result_box = result.unwrap().unwrap().into_boxed_slice();
// return boxed slice from the future
        result_box
      };
// return the future
    Op::Async(fut.boxed())
}

A future is created using the async block and returned as a boxed future. Deno handles the completion of the future and informs the Deno side of the plugin. A channel is used to communicate between the new thread and the main thread.

The Deno code doesn’t need much updating — just a new asyncHandler to handle the completion of the task:

let path = ""
if (Deno.build.os === "linux") {
  path = "./native/target/debug/libnative.so"
} else if (Deno.build.os === "windows") {
  path = "./native/target/debug/native.dll"
} else if (Deno.build.os === "darwin") {
  path = "./native/target/debug/libnative.dylib"
}
const rid = Deno.openPlugin(path);

const { optimise_async } = (Deno as any).core.ops();

export async function optimise(fileName: string){
  const file = await Deno.open(fileName);
  const value = await Deno.readAll(file);
  await Deno.close(file.rid);
// new handler
  (Deno as any).core.setAsyncHandler(optimise_async, (response:any) => {
    Deno.writeFile("l.png",response)
  });
// executing the native code.
  (Deno as any).core.dispatch(optimise_async,value);
}
await optimise("t.png")

await Deno.close(rid);

Conclusion

In this tutorial, we covered how to build a simple Deno plugin using Rust as well as how to create an async plugin using Rust futures and the deno_core crate.

Rust has a large ecosystem with high-quality crates. You can use all these crates in Deno by creating plugins. Whether it’s an image processing plugin, database connector, etc., access to Rust plugins helps to expand the Deno ecosystem.

#rust #deno #typescript #javascript

What is GEEK

Buddha Community

How to Build a Deno Plugin using Rust

Why Use WordPress? What Can You Do With WordPress?

Can you use WordPress for anything other than blogging? To your surprise, yes. WordPress is more than just a blogging tool, and it has helped thousands of websites and web applications to thrive. The use of WordPress powers around 40% of online projects, and today in our blog, we would visit some amazing uses of WordPress other than blogging.
What Is The Use Of WordPress?

WordPress is the most popular website platform in the world. It is the first choice of businesses that want to set a feature-rich and dynamic Content Management System. So, if you ask what WordPress is used for, the answer is – everything. It is a super-flexible, feature-rich and secure platform that offers everything to build unique websites and applications. Let’s start knowing them:

1. Multiple Websites Under A Single Installation
WordPress Multisite allows you to develop multiple sites from a single WordPress installation. You can download WordPress and start building websites you want to launch under a single server. Literally speaking, you can handle hundreds of sites from one single dashboard, which now needs applause.
It is a highly efficient platform that allows you to easily run several websites under the same login credentials. One of the best things about WordPress is the themes it has to offer. You can simply download them and plugin for various sites and save space on sites without losing their speed.

2. WordPress Social Network
WordPress can be used for high-end projects such as Social Media Network. If you don’t have the money and patience to hire a coder and invest months in building a feature-rich social media site, go for WordPress. It is one of the most amazing uses of WordPress. Its stunning CMS is unbeatable. And you can build sites as good as Facebook or Reddit etc. It can just make the process a lot easier.
To set up a social media network, you would have to download a WordPress Plugin called BuddyPress. It would allow you to connect a community page with ease and would provide all the necessary features of a community or social media. It has direct messaging, activity stream, user groups, extended profiles, and so much more. You just have to download and configure it.
If BuddyPress doesn’t meet all your needs, don’t give up on your dreams. You can try out WP Symposium or PeepSo. There are also several themes you can use to build a social network.

3. Create A Forum For Your Brand’s Community
Communities are very important for your business. They help you stay in constant connection with your users and consumers. And allow you to turn them into a loyal customer base. Meanwhile, there are many good technologies that can be used for building a community page – the good old WordPress is still the best.
It is the best community development technology. If you want to build your online community, you need to consider all the amazing features you get with WordPress. Plugins such as BB Press is an open-source, template-driven PHP/ MySQL forum software. It is very simple and doesn’t hamper the experience of the website.
Other tools such as wpFoRo and Asgaros Forum are equally good for creating a community blog. They are lightweight tools that are easy to manage and integrate with your WordPress site easily. However, there is only one tiny problem; you need to have some technical knowledge to build a WordPress Community blog page.

4. Shortcodes
Since we gave you a problem in the previous section, we would also give you a perfect solution for it. You might not know to code, but you have shortcodes. Shortcodes help you execute functions without having to code. It is an easy way to build an amazing website, add new features, customize plugins easily. They are short lines of code, and rather than memorizing multiple lines; you can have zero technical knowledge and start building a feature-rich website or application.
There are also plugins like Shortcoder, Shortcodes Ultimate, and the Basics available on WordPress that can be used, and you would not even have to remember the shortcodes.

5. Build Online Stores
If you still think about why to use WordPress, use it to build an online store. You can start selling your goods online and start selling. It is an affordable technology that helps you build a feature-rich eCommerce store with WordPress.
WooCommerce is an extension of WordPress and is one of the most used eCommerce solutions. WooCommerce holds a 28% share of the global market and is one of the best ways to set up an online store. It allows you to build user-friendly and professional online stores and has thousands of free and paid extensions. Moreover as an open-source platform, and you don’t have to pay for the license.
Apart from WooCommerce, there are Easy Digital Downloads, iThemes Exchange, Shopify eCommerce plugin, and so much more available.

6. Security Features
WordPress takes security very seriously. It offers tons of external solutions that help you in safeguarding your WordPress site. While there is no way to ensure 100% security, it provides regular updates with security patches and provides several plugins to help with backups, two-factor authorization, and more.
By choosing hosting providers like WP Engine, you can improve the security of the website. It helps in threat detection, manage patching and updates, and internal security audits for the customers, and so much more.

Read More

#use of wordpress #use wordpress for business website #use wordpress for website #what is use of wordpress #why use wordpress #why use wordpress to build a website

Tamia  Walter

Tamia Walter

1622702379

How to Build a Deno Plugin using Rust

This tutorial covers how to build a Deno plugin using Rust and create an async plugin using Rust futures and the deno_core crate.

Deno is a new JavaScript runtime built with Rust and V8 that enables you to run JavaScript outside the browser.  Deno is more secure than Node.js because it limits network and file system access by default.

One of the cool things about Deno is that you can write plugins in Rust and use them within Deno code. In this tutorial, we’ll show you how to create Deno plugins in Rust.

Originally published by Anshul Goyal on https://blog.logrocket.com

We’ll cover the following:

  • Why write Deno plugins in Rust?
  • Deno plugin project structure
  • Building a Rust project
  • Adding Rust code
  • Creating the optimizer function
  • Loading a Rust plugin in Deno
  • Writing async plugins

Why write Deno plugins in Rust?

Plugins in Deno generally improve performance and provide access to a wider range of tools.

Due to their performant nature, plugins are often used in calculations for heavy tasks such as image processing. Plugins also give you access to a variety of libraries written in other languages, including high-quality Rust crates.

Deno plugin project structure

The plugin project structure is the same as any Deno module. For the purpose of this tutorial, you can use this boilerplate:

git clone https://github.com/anshulrgoyal/deno-rust-starter.git my_module

First, build the Rust boilerplate for the plugin:

cd my_module/native
cargo build

Next, run a test to verify that Deno is picking up the correct library:

cd my_module/native
deno test --unstable --allow-plugin

The boilerplate includes a Rust project in the native directory and a Deno module in the root.

Building a Rust project

The Rust project compiles a dynamic library that is loaded by the Deno runtime. The file type and name of the library depend on the operating system. The Rust project may compile to a so file — dylib or dll — and the name of the compiled file may also be different. The boilerplate can handle three major platforms: Linux, macOS, and Windows.

[package]
name = "native"
version = "0.1.0"
authors = ["anshul <anshulgoel151999@gmail.com>"]
edition = "2018"

[lib]
name = "native"
crate-type = ["cdylib"]

[dependencies]
deno_core = "0.75.0"

├── README.md
├── deps.ts
├── mod.ts
├── mod_test.ts
├── native
│   ├── Cargo.lock
│   ├── Cargo.toml
│   ├── src
│       └── lib.rs
├── test.ts
├── test_deps.ts
└── tsconfig.json

The mod.ts file is the main file imported by another application using your module.

Adding Rust code

For this tutorial, we’ll show you how to build a PNG optimizer using an oxipng crate. Every Deno plugin must export the deno_plugin_init function and register all the methods that the plugin exports.

The #[no_mangle] attribute tells the compiler not to change the name of the function:

#[no_mangle]
pub fn deno_plugin_init(interface: &mut dyn Interface) {
 // register the function. Pass name and function to register method
    interface.register_op("hello_world",hello_world);
}

Creating the optimizer function

Each exported function has the same signature. Deno plugins can only export functions. These functions can be sync or async, depending on the return type.

fn optimise(_interface: &mut dyn Interface,
    zero_copy: &mut [ZeroCopyBuf],
) -> Op {
    // get first argument
    let first=zero_copy.first().unwrap();
    let opts: oxipng::Options = Default::default();
    // convert vector
    let result = oxipng::optimize_from_memory(&first.to_vec(), &opts).unwrap();
    // move to heap so that deno can use it
    Op::Sync(Box::from(result))
}

The second argument of the function contains an array of buffers. Each buffer in the array represents the argument passed to the exported function when called. These buffers are serialized to strings or other data types based on requirements.

The above code takes the first element of zero_copy and passes it to optimize_from_memory. The first element of the array is the file passed to the optimize function when called from the Deno code. The file is passed as bytes. The function processes the file and returns the result as a Box. The return type is Op enum with two variants sync and async.

Build the code using the cargo build command. Now this plugin can be used in Deno.

Loading a Rust plugin in Deno

Now that the plugin is compiled, let’s load it using Deno.

The plugin is still in development and is a part of unstable APIs, so the --unstable flag is required, as is --allow-plugin.

let path = ""
// check the type of OS to load correct file
if (Deno.build.os === "linux") {
// linux file emited by rust compiler
  path = "./native/target/debug/libnative.so"
} else if (Deno.build.os === "windows") {
// windows file emited by rust compiler
  path = "./native/target/debug/native.dll"
} else if (Deno.build.os === "darwin") {
// macos file emited by rust comipler
  path = "./native/target/debug/libnative.dylib"
}
// load plugin from file system
const rid = Deno.openPlugin(path);
// Get available methods on plugin
//@ts-Expect-Error
const { optimise:optimise_native } = (Deno as any).core.ops();

export async function optimise(fileName: string): Promise<Uint8Array> {
// reading a file
  const file = await Deno.open(fileName);
// getting content
  const value = await Deno.readAll(file)
// closing file
  await Deno.close(file.rid)
// running the native plugin method using Deno dispatch method
  return (Deno as any).core.dispatch(optimise_native, value)
}

Each plugin is loaded using the openPlugin method. Then, the ops method is used to get the method identifier, which executes the code exported by the plugin.

dispatch is used to run code exported by the native plugin. The first argument is the method identifier; the rest are passed for the native function. In this case, the file is passed.

Writing async plugins

Since Deno is single-threaded, it’s not wise to block the main thread. Deno allows you to return a future from the native function, which you can use with OS threads to write a function that doesn’t block the main thread.

fn optimise_async(_interface: &mut dyn Interface,
    zero_copy: &mut [ZeroCopyBuf],
) -> Op {
// get first argument
    let first=zero_copy.first().unwrap();
    let opts: oxipng::Options = Default::default();
    let arg=first.to_vec();
// create a new future
    let fut = async move {
// create a channel to send result once done to main thread
        let (tx, rx) = futures::channel::oneshot::channel::<oxipng::PngResult<Vec<u8>>>();
// create a new thread
        std::thread::spawn(move || {
// perform work
          let result = oxipng::optimize_from_memory(&arg, &opts);
// send result to main thread
          tx.send(result).unwrap();
        });
// receive the result
        let result=rx.await;
// create a boxed slice
        let result_box = result.unwrap().unwrap().into_boxed_slice();
// return boxed slice from the future
        result_box
      };
// return the future
    Op::Async(fut.boxed())
}

A future is created using the async block and returned as a boxed future. Deno handles the completion of the future and informs the Deno side of the plugin. A channel is used to communicate between the new thread and the main thread.

The Deno code doesn’t need much updating — just a new asyncHandler to handle the completion of the task:

let path = ""
if (Deno.build.os === "linux") {
  path = "./native/target/debug/libnative.so"
} else if (Deno.build.os === "windows") {
  path = "./native/target/debug/native.dll"
} else if (Deno.build.os === "darwin") {
  path = "./native/target/debug/libnative.dylib"
}
const rid = Deno.openPlugin(path);

const { optimise_async } = (Deno as any).core.ops();

export async function optimise(fileName: string){
  const file = await Deno.open(fileName);
  const value = await Deno.readAll(file);
  await Deno.close(file.rid);
// new handler
  (Deno as any).core.setAsyncHandler(optimise_async, (response:any) => {
    Deno.writeFile("l.png",response)
  });
// executing the native code.
  (Deno as any).core.dispatch(optimise_async,value);
}
await optimise("t.png")

await Deno.close(rid);

Conclusion

In this tutorial, we covered how to build a simple Deno plugin using Rust as well as how to create an async plugin using Rust futures and the deno_core crate.

Rust has a large ecosystem with high-quality crates. You can use all these crates in Deno by creating plugins. Whether it’s an image processing plugin, database connector, etc., access to Rust plugins helps to expand the Deno ecosystem.

#rust #deno #typescript #javascript

How To Customize WordPress Plugins? (4 Easy Ways To Do)

This is image title
WordPress needs no introduction. It has been in the world for quite a long time. And up till now, it has given a tough fight to leading web development technology. The main reason behind its remarkable success is, it is highly customizable and also SEO-friendly. Other benefits include open-source technology, security, user-friendliness, and the thousands of free plugins it offers.

Talking of WordPress plugins, are a piece of software that enables you to add more features to the website. They are easy to integrate into your website and don’t hamper the performance of the site. WordPress, as a leading technology, has to offer many out-of-the-box plugins.

However, not always the WordPress would be able to meet your all needs. Hence you have to customize the WordPress plugin to provide you the functionality you wished. WordPress Plugins are easy to install and customize. You don’t have to build the solution from scratch and that’s one of the reasons why small and medium-sized businesses love it. It doesn’t need a hefty investment or the hiring of an in-house development team. You can use the core functionality of the plugin and expand it as your like.

In this blog, we would be talking in-depth about plugins and how to customize WordPress plugins to improve the functionality of your web applications.

What Is The Working Of The WordPress Plugins?

Developing your own plugin requires you to have some knowledge of the way they work. It ensures the better functioning of the customized plugins and avoids any mistakes that can hamper the experience on your site.

1. Hooks

Plugins operate primarily using hooks. As a hook attaches you to something, the same way a feature or functionality is hooked to your website. The piece of code interacts with the other components present on the website. There are two types of hooks: a. Action and b. Filter.

A. Action

If you want something to happen at a particular time, you need to use a WordPress “action” hook. With actions, you can add, change and improve the functionality of your plugin. It allows you to attach a new action that can be triggered by your users on the website.

There are several predefined actions available on WordPress, custom WordPress plugin development also allows you to develop your own action. This way you can make your plugin function as your want. It also allows you to set values for which the hook function. The add_ action function will then connect that function to a specific action.

B. Filters

They are the type of hooks that are accepted to a single variable or a series of variables. It sends them back after they have modified it. It allows you to change the content displayed to the user.

You can add the filter on your website with the apply_filter function, then you can define the filter under the function. To add a filter hook on the website, you have to add the $tag (the filter name) and $value (the filtered value or variable), this allows the hook to work. Also, you can add extra function values under $var.

Once you have made your filter, you can execute it with the add_filter function. This will activate your filter and would work when a specific function is triggered. You can also manipulate the variable and return it.

2. Shortcodes

Shortcodes are a good way to create and display the custom functionality of your website to visitors. They are client-side bits of code. They can be placed in the posts and pages like in the menu and widgets, etc.

There are many plugins that use shortcodes. By creating your very own shortcode, you too can customize the WordPress plugin. You can create your own shortcode with the add_shortcode function. The name of the shortcode that you use would be the first variable and the second variable would be the output of it when it is triggered. The output can be – attributes, content, and name.

3. Widgets

Other than the hooks and shortcodes, you can use the widgets to add functionality to the site. WordPress Widgets are a good way to create a widget by extending the WP_Widget class. They render a user-friendly experience, as they have an object-oriented design approach and the functions and values are stored in a single entity.

How To Customize WordPress Plugins?

There are various methods to customize the WordPress plugins. Depending on your need, and the degree of customization you wish to make in the plugin, choose the right option for you. Also, don’t forget to keep in mind that it requires a little bit of technical knowledge too. So find an expert WordPress plugin development company in case you lack the knowledge to do it by yourself.

1. Hire A Plugin Developer3
This is image title

One of the best ways to customize a WordPress plugin is by hiring a plugin developer. There are many plugin developers listed in the WordPress directory. You can contact them and collaborate with world-class WordPress developers. It is quite easy to find a WordPress plugin developer.

Since it is not much work and doesn’t pay well or for the long term a lot of developers would be unwilling to collaborate but, you will eventually find people.

2. Creating A Supporting Plugin

If you are looking for added functionality in an already existing plugin go for this option. It is a cheap way to meet your needs and creating a supporting plugin takes very little time as it has very limited needs. Furthermore, you can extend a plugin to a current feature set without altering its base code.

However, to do so, you have to hire a WordPress developer as it also requires some technical knowledge.

3. Use Custom Hooks

Use the WordPress hooks to integrate some other feature into an existing plugin. You can add an action or a filter as per your need and improve the functionality of the website.

If the plugin you want to customize has the hook, you don’t have to do much to customize it. You can write your own plugin that works with these hooks. This way you don’t have to build a WordPress plugin right from scratch. If the hook is not present in the plugin code, you can contact a WordPress developer or write the code yourself. It may take some time, but it works.

Once the hook is added, you just have to manually patch each one upon the release of the new plugin update.

4. Override Callbacks

The last way to customize WordPress plugins is by override callbacks. You can alter the core functionality of the WordPress plugin with this method. You can completely change the way it functions with your website. It is a way to completely transform the plugin. By adding your own custom callbacks, you can create the exact functionality you desire.

We suggest you go for a web developer proficient in WordPress as this requires a good amount of technical knowledge and the working of a plugin.

Read More

#customize wordpress plugins #how to customize plugins in wordpress #how to customize wordpress plugins #how to edit plugins in wordpress #how to edit wordpress plugins #wordpress plugin customization

Toby Rogers

Toby Rogers

1602123065

How to Build URL Shortener using Deno

In this article, we’re going to learn the basics of Deno, like how to run a program and embrace security.

Deno is the new JavaScript and TypeScript runtime written in Rust. It offers tight security, TypeScript support out-of-the-box, a single executable to run it, and a set of reviewed and audited standard modules.

Like npm in Node.js, packages in Deno are managed in a centralized package repository called X. We’ll be using one of these libraries, Oak, to build a REST API-based server in Deno.

After learning the basics by using the Express-like router package Oak, we will jump into the deep end of Deno and build a complete application.

Here’s what we will set up in this application:

  1. Mapping URL shortcodes to endpoints using a JSON-based config file.
  2. Have expiration dates attached to each URL so that these redirects are only valid for a limited period of time.

0. Prerequisites

  1. Install Deno from this link.
  2. Make sure you know the basics of JavaScript.

So, let’s get started. 🔥

1. How to Build the Router

To write the server-side code for our application, we’ll use the Oak module. It has an Express-like syntax for defining API routes.

If we look at its documentation here, the “Basic Usage” section pretty much covers all the use cases we will need in our router. So, we will expand on that code to build our application.

To test this code, you can create a file called index.ts in a folder, and copy the “Basic Usage” code into it.

To understand how to run TypeScript or JavaScript files in Deno, you first need to understand how Deno runs files.

You run a file by running the command deno run file_name.ts or file_name.js, followed by a set of flags providing certain system permissions to your application.

To test this, run the file we just created, containing the “Basic Usage” code, by using the command deno run index.ts.

You will see that Deno complains that you haven’t given network access to your application. So, to do that, you add the -allow-net to the run command. The command will look like deno run index.ts -allow-net.

The router written down in the "Basic Usage” code looks like this:

router
  .get("/", (context) => {
    context.response.body = "Hello world!";
  })
  .get("/book", (context) => {
    context.response.body = Array.from(books.values());
  })
  .get("/book/:id", (context) => {
    if (context.params && context.params.id && books.has(context.params.id)) {
      context.response.body = books.get(context.params.id);
    }
  });

To break down the above code, first a router object has been defined. Then the get function is called on the router, to define the various endpoints for our application. The respective logic is defined inside the callback functions.

For example, for the “/” endpoint, a callback function which returns “Hello World” in the response body has been defined. We can keep this endpoint unchanged to test whether our application server is running by receiving this response.

We don’t need the “/book” URL which has been defined, so its definition can be safely removed. At this point, your router should have the below structure:

router
  .get("/", (context) => {
    context.response.body = "Hello world!";
  })
  .get("/book/:id", (context) => {
    if (context.params && context.params.id && books.has(context.params.id)) {
      context.response.body = books.get(context.params.id);
    }
  });

In the next section, we’ll be focussing on starting to build the actual application.

2. How to Build the URL Shortener

Now let’s get started with building the actual URL shortener.

It should redirect to a destination (dest), based on a shortcode. The redirect should also only be valid up to an expiryDate, which can be provided in the Year-Month-Day format.

Based on these assumptions, let’s create the config file, named urls.json. The format of the file will be:

{
  "shortcode": {
    "dest": "destination_url_string",
    "expiryDate": "YYYY-MM-DD"
  }
}

You can check out the JSON file here.

To read this JSON file in your code, add the following to the top of your index.ts:

import { Application, Router } from "<https://deno.land/x/oak/mod.ts>";

const urls = JSON.parse(Deno.readTextFileSync("./urls.json"));

console.log(urls);

Now, to run your index.ts, you will need another flag —allow-read, otherwise Deno will throw a “read permissions not provided” error. Your final command becomes deno run —allow-net —allow-read index.ts.

After running this command, you’ll see the JSON file being printed in your terminal window. This means that your program is able to read the JSON file correctly.

If we go back to the “Basic Usage” example that we saw above, the route “/book/:id” is exactly what we need.

Instead of “/book/:id”, we can use “/shrt/:urlid”, where we will get the individual URLs based on the URL ID (:urlid).

Replace the existing code present inside the “/book/:id” route with this:

.get("/shrt/:urlid", (context) => {
    if (context.params && context.params.urlid && urls[context.params.urlid]) {
      context.response.redirect(urls[context.params.urlid].dest);
    } else {
      context.response.body = "404";
    }
  });

The if condition in the route does the following:

  1. Checks if parameters are attached to the route
  2. Checks if the parameter urlid is in the parameter list.
  3. Checks whether the urlid matches with any URL in our JSON.

If it matches with all these, the user is redirected to the correct URL. If it doesn’t, a 404 response on the body is returned.

To test this, copy this route into index.ts. The router will now look like this:

router
  .get("/", (context) => {
    context.response.body = "Hello world!";
  })
	.get("/shrt/:urlid", (context) => {
	    if (context.params && context.params.urlid && urls[context.params.urlid]) {
	      context.response.redirect(urls[context.params.urlid].dest);
	    } else {
	      context.response.body = "404";
	    }
	  });

And run the file using deno run —allow-net —allow-read index.ts.

If you copied the JSON file from the example, and if you go to http://localhost:8000/shrt/g, you’ll be redirected to Google’s homepage.

On the other hand, if you use a random shortcode that doesn’t work in our URL’s config, it brings you to the 404 page.

However, you’ll see that our shortener doesn’t react live to changes in the JSON file. To test this, try adding a new redirect to urls.json in the same format as:

"shortcode": {
    "dest": "destination_url_string",
    "expiryDate": "YYYY-MM-DD"
  }

The reason for this is that urls.json is only read once at that start. So, now we will add live-reloading to our server.

3. How to Add Live-Reloading

To make the urls object react live to changes in the JSON file, we simply move the read statement inside our route. This should look like the following:

.get("/shrt/:urlid", (context) => {
  const urls = JSON.parse(Deno.readTextFileSync("./urls.json"));

  if (context.params && context.params.urlid && urls[context.params.urlid]) {
    context.response.redirect(urls[context.params.urlid].dest);
  } else {
    context.response.body = "404";
  }
});

Note how we have moved the URLs object inside our router. Now in this case, the config file is read every time that route is called, so it can react live to any changes made in the urls.json file. So even if we add or remove other redirects on the fly, our code reacts to it.

4. How to Add an Expiration to the URLs

To make our URLs expire at a certain date, we will be using the popular Moment.js library, which makes it easy to work with dates.

Luckily, it has also been ported for Deno. To understand how it works, check out its documentation in the previous link.

To use it in our code, import it directly through its URL like this:

import { Application, Router } from "<https://deno.land/x/oak/mod.ts>";
import { moment } from "<https://deno.land/x/moment/moment.ts>";

const router = new Router();

To check the date for when the URL will expire, we check the expiryDate key on our urls object. This will make the code look like this:

if (context.params && context.params.urlid && urls[context.params.urlid]) {
  if (
    urls[context.params.urlid].expiryDate > moment().format("YYYY-MM-DD")
  ) {
    context.response.redirect(urls[context.params.urlid].dest);
  } else {
    context.response.body = "Link Expired";
  }
} else {
  context.response.body = "404";
}

In moment().format("YYYY-MM-DD"), we get the current date and time using moment(). We can convert it to the “YYYY-MM-DD” (Year-Month-Date) format using the function .format("YYYY-MM-DD").

By comparing it against our expiryDate key, we can check whether the URL has expired or not.

That’s it! You have built a fully functional URL shortener in Deno. You can find the final code in the GitHub repo here.

Test it out by setting expiryDate as the current date and by making other changes to urls.json and our code.

My Thoughts on Deno

To wrap the article up, I’ll put my forward final thoughts on deno.land.

While it’s refreshing to see a server-side language which takes security into consideration and supports TypeScript out-of-the-box, Deno still has a long way to go before being ready for use in production systems.

For example, the TypeScript compilation is still very slow, with compilation times ~20 seconds, even for simple programs like the one we just developed.

On the error-reporting front, it still is pretty bad with describing the errors. For example, while embedding the code to read urls.json in the function itself, Deno isn’t able to report that the -allow-read flag hasn’t been set. Instead, it just throws an internal server error without a proper error printed on the terminal.

Important Links

Deno - https://deno.land

Deno X (package repository) - https://deno.land/x/

Oak (REST framework) - https://deno.land/x/oak

Oak Basic Usage - https://deno.land/x/oak@v6.3.1#basic-usage

Final GitHub Repo - https://github.com/akash-joshi/deno-url-shortener

Thank you for reading this far and happy programming 🥳 !!

#deno #javascript #typescript #rust #web-development

Zara  Bryant

Zara Bryant

1595207546

Calcite - A Rust Framework for Creating Deno Plugins

Calcite - A Rust Framework for Creating Deno Plugins
Source: https://github.com/Srinivasa314/calcite/tree/master/docs

#deno #rust #developer