How to register a url protocol handler in Node.js

13.8k views Asked by At

I am developing a command line node module and would like to be able to launch it via links on a website.

I want to register a custom protocol my-module:// such that links would have the following format: my-module://action:some-action and clicking on them would start the node package.

If there isn't a node API for this (I'm sure there won't be) then is there a way I can do it from node by invoking system commands?

It must work on Windows, Linux, and MacOS.

3

There are 3 answers

3
jeremy On BEST ANSWER

Its an interesting idea. I don't think there is currently a cross platform node.js solution out there. I did come across this thread of people asking for the same thing:

https://github.com/rogerwang/node-webkit/issues/951

Electron now supports it with the app.setAsDefaultProtocolClient API (since v0.37.4) for macOS and Windows.

It wouldn't be terribly difficult to write the library to do this.

Windows:

On the windows side you'd have to register the app as the application that handles that URI scheme.

You'll need to set up a registry entry for your application:

HKEY_CLASSES_ROOT
   alert
      (Default) = "URL:Alert Protocol"
      URL Protocol = ""
      DefaultIcon
         (Default) = "alert.exe,1"
      shell
         open
            command
               (Default) = "C:\Program Files\Alert\alert.exe" "%1"

Then, when your application is run by windows, you should be able to see the arguments in process.argv[]. Make sure that you launch a shell to run node, not just your application directly.

Original MSDN article

Note this requires administrator privileges and sets the handler system-wide. To do it per user, you can use HKEY_CURRENT_USER\Software\Classes instead, as the Electron's implementation does it.

Apple:

The cited "OS X" article in the github comment is actually for iOS. I'd look at the following programming guide for info on registering an application to handle a URL scheme:

Apple Dev Documentation

In summary, you'll need to create a launch service and populate the .plist file with CFBundleURLTypes, this field is an array and should be populated with just the protocol name i.e. http

The following Super User Question has a better solution, but is a per user setting.

"The file you seek is ~/Library/Preferences/com.apple.LaunchServices.plist.

It holds an array called LSHandlers, and the Dictionary children that define an LSHandlerURLScheme can be modified accordingly with the LSHandlerRole."

Linux:

From what I can tell, there are several ways to accomplish this in Linux (surprise?)

Gnome has a tool that will let you register a url handler w3 archives

gconftool-2 -t string -s /desktop/gnome/url-handlers/tel/command "bin/vonage-call %s"
gconftool-2 -s /desktop/gnome/url-handlers/tel/needs_terminal false -t bool
gconftool-2 -t bool -s /desktop/gnome/url-handlers/tel/enabled true

Some of the lighter weight managers look like they allow you to create fake mime types and register them as URI Protocol handlers.

"Fake mime-types are created for URIs with various scheme like this: application/x-xdg-protocol- Applications supporting specific URI protocol can add the fake mime-type to their MimeType key in their desktop entry files. So it's easy to find out all applications installed on the system supporting a URI scheme by looking in mimeinfo.cache file. Again defaults.list file can be used to specify a default program for speficied URI type." wiki.lxde.org

KDE also supports their own method of handling URL Protocol Handlers:

Create a file: $KDEDIR/share/services/your.protocol and populate it with relevant data:

[Protocol]
exec=/path/to/player "%u"
protocol=lastfm
input=none
output=none
helper=true
listing=
reading=false
writing=false
makedir=false
deleting=false

from last.fm forums of all places

Hope that helps.

0
Shubham Kumar On

Edit :

Looks like the module has changed the registration process for good:

const path = require('path');

const ProtocolRegistry = require('protocol-registry');

console.log('Registering...');
// Registers the Protocol
ProtocolRegistry.register({
    protocol: 'testproto', // sets protocol for your command , testproto://**
    command: `node ${path.join(__dirname, './tester.js')} $_URL_`, // this will be executed with a extra argument %url from which it was initiated
    override: true, // Use this with caution as it will destroy all previous Registrations on this protocol
    terminal: true, // Use this to run your command inside a terminal
    script: false
}).then(async () => {
    console.log('Successfully registered');
});

Original Answer :

There is an npm module for this purpose.

link :https://www.npmjs.com/package/protocol-registry

So to do this in nodejs you just need to run the code below:

First Install it

npm i protocol-registry

Then use the code below to register you entry file.

const path = require('path');

const ProtocolRegistry = require('protocol-registry');

console.log('Registering...');
// Registers the Protocol
ProtocolRegistry.register({
    protocol: 'testproto', // set your app for testproto://**
    command: `node ${path.join(__dirname, './index.js')}`, // this will be executed with a extra argument %url from which it was initiated
}).then(async () => {
    console.log('Successfully registered');
});

Then suppose someone opens testproto://test then a new terminal will be launched executing :

node yourapp/index.js testproto://test
1
Freez On

Here's how I did on Mac OS with an application NW.js :

  1. Open the app /Applications/Utilities/Script Editor

    type the following code in the editor

    on open location this_URL
       do shell script "/Applications/X.app/Contents/MacOS/x '" & this_URL & "'"
    end open location
    

    Replace X by the name of your App.

    Save the script as an Application Bundle anywhere

  2. Go to the script, right click then 'Show Package Contents' then edit Contents/info.plist

    Add these lines at the end of the file, just before </dict></plist> :

    <key>CFBundleIdentifier</key>
    <string>com.mycompany.AppleScript.AppName</string> <!-- edit here -->
    <key>CFBundleURLTypes</key>
    <array>
      <dict>
        <key>CFBundleURLName</key>
        <string>AppName</string> <!-- edit here -->
        <key>CFBundleURLSchemes</key>
        <array>
          <string>myurlscheme</string> <!-- your url scheme here -->
        </array>
      </dict>
    </array>
    
  3. You can now open a link starting with myurlscheme: and see your app is opening!