npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@dodi-smart/nuki-graphql-api

v1.2.0

Published

Nuki GraphQL API

Downloads

4

Readme

This package creates a Nuki GraphQL API, allowing for interaction with data at Nuki. It is unofficial Nuki GraphQL API generated based on Nuki's Swagger OpenAPI specification, mainly configured to be used as Hasura Remote Schema.

Here's an example of how to use the Nuki GraphQL API to get a list of smart locks of the configured Nuki user:

query MyLocks {
  nuki {
    smartlocks {
      name
      smartlockId
      state {
        batteryCharge
        state
      }
      firmwareVersion
      hardwareVersion
    }
  }
}

It's recommended to add the Nuki GraphQL API as a Remote Schema in Hasura and connect data from your database with data in Nuki. By doing so, it's possible to request data from your database and Nuki in a single GraphQL query.

Here's an example of how to use the Nuki GraphQL API to get a list of smart locks for a specific Nuki user. Note that the user data is fetched from your database and the Nuki user data is fetched trough the Nuki API:

query UsersLocks {
  users {
    id
    displayName
    nukies {
      # Remote schema relation in users table
      name
      smartlockId
      state {
        batteryCharge
        state
      }
      firmwareVersion
      hardwareVersion
    }
  }
}

Supported APIs

APIs can be added upon request. PRs are welcome of course!

https://api.nuki.io

| Api | Supported | Comment | | ------------------- | :-------: | ----------------------------------------- | | Account | 🟠 | | | AccountSubscription | 🔴 | | | AccountUser | 🔴 | | | Address | 🔴 | | | AddressReservation | 🔴 | | | AddressToken | 🔴 | | | AdvancedApi | 🟠 | | | ApiKey | 🔴 | | | Company | 🔴 | | | Notification | 🔴 | | | Opener | 🔴 | | | Service | 🔴 | | | Smartlock | 🟠 | | | SmartlockAuth | 🔴 | | | SmartlockLog | 🟢 | Available nested inside smartlock query | | Subscription | 🔴 | |

🟢 - Supported 🟠 - Partial 🔴 - Not supported

Getting Started

Install the package:

pnpm add @dodi-smart/nuki-graphql-api

Setup serverless function

Create a new Serverless Function: functions/graphql/nuki.ts:

import { createNukiGraphQLServer } from '@dodi-smart/nuki-graphql-api'

const server = createNukiGraphQLServer({
  graphiql: true,
  graphqlEndpoint: '/graphql/nuki',
  healthCheckEndpoint: '/graphql/nuki/health',
  provideAuth(context) {
    return {
      // Should provide authentication
    }
  },
  onUpdateSession(context, auth) {
    // Handle session update
  }
})

export default server

Configure Nuki Web

Go to Nuki Web and obtain OAuth2 API key and secret. Click on Generate API token, give the token name and check the scopes you want to use. Click save and copy the token value.

  • Add NUKI_CLIENT_ID as an environment variable.
  • Add NUKI_CLIENT_SECERT as an environment variable.
  • Add NUKI_API_KEY as an environment variable.

If you're using Nhost, add NUKI_CLIENT_ID, NUKI_CLIENT_SECERT and NUKI_API_KEY to .env.development like this:

NUKI_CLIENT_ID=6EYY••••
NUKI_CLIENT_SECERT=BPum••••
NUKI_API_KEY=59f6d••••

And add the production key values to environment variables in the Nhost dashboard.

Start Nhost

nhost up

Learn more about the Nhost CLI.

Test

Test the Nuki GraphQL API in the browser:

https://local.functions.nhost.run/v1/graphql/nuki

Remote Schema

Add the Nuki GraphQL API as a Remote Schema in Hasura.

URL

{{NHOST_FUNCTIONS_URL}}/graphql/nuki

Headers

x-nhost-webhook-secret: NHOST_WEBHOOK_SECRET (From env var)

The NHOST_WEBHOOK_SECRET is used to verify that the request is coming from Nhost. The environment variable is a system environment variable and is always available.

Authentication

You have several options to authentication your users agains Nuki using this library. All of them depends on the usecase:

Single user usecase (easy to setup)

When the system should work with the locks of a single user (administrator). Permissions and access to the locks are granted to others users via the administrator or using Hasura user roles.

Multi user usecase (advanced)

When system work with locks of multiple users. Each user can authenticate against Nuki using their own credentials via OAuth 2.0. This usecase can be used for multi tenant apps for example.

How to choose

| Use case | Using API_KEY (easy) | OAuth 2.0 (advanced) | OAuth 2.0 + ApiKeyToken (advanced) | | ----------- | :------------------: | :------------------: | :--------------------------------: | | Single user | 🟢 | 🟠 | 🟠 | | Multi user | 🔴 | 🟠 | 🟢 |

🟢 - Preferable way 🟠 - Supported 🔴 - Not supported

Using API_KEY

Go to Nuki Web and click on API on side navigation

Click on Generate API token, give the token name and check the scopes you want to use. Click save and copy the token value. ApiKeyToken is long-living token and does not require refresh.

  • Add NUKI_API_KEY as an environment variable.

If you're using Nhost, add NUKI_API_KEY to .env.development like this:

NUKI_API_KEY=59f6d••••

And add the production key values to environment variables in the Nhost dashboard.

(Optional) Modify Remote Schema

If you need to pass the API key from Hasura instead of having it as environment variable, then:

Add to the Hasura Remote Schema the API Key

x-nuki-api-key: NUKI_API_KEY (From env var)

The environment variable is a system environment variable and is always available.

Configure the Nuki GraphQL API

import { createNukiGraphQLServer } from '@dodi-smart/nuki-graphql-api'

const server = createNukiGraphQLServer({
  graphqlEndpoint: '/graphql/nuki',
  healthCheckEndpoint: '/graphql/nuki/health',
  provideAuth(context) {
    const { request } = context
    return {
      // Is enough to pass just the accessToken
      accessToken: request.headers.get('x-nuki-api-key')
    }
  },
  onUpdateSession(context, auth) {
    // Can be empty as NUKI_API_KEY are long-living tokens
  }
})

export default server

OAuth 2.0

Go to Nuki Web and click on API on side navigation

Point OAuth2 Redirect URL to a Nhost serverless function:

https://local.functions.nhost.run/v1/callback/nuki

Use callback helper function to get session

Create a new Serverless Function: functions/callback/nuki.ts:

import { createNukiOAuthCallback } from '@dodi-smart/nuki-graphql-api'

const handler = (req, res) => {
  const {userId, accessToken, refreshToken} = createNukiOAuthCallback(req, res)
  await gqlSDK.updateUserNukiAuth({
    id: userId,
    accessToken,
    refreshToken
  })
}

export default handler

Configure the Nuki GraphQL API

import { createNukiGraphQLServer } from '@dodi-smart/nuki-graphql-api'

const server = createNukiGraphQLServer({
  graphqlEndpoint: '/graphql/nuki',
  healthCheckEndpoint: '/graphql/nuki/health',
  provideAuth(context) {
    const { userClaims } = context

    const { user } = await gqlSDK.getUser({
      id: userId
    })

    return {
      // Example if nuki creds are saved in user's metadata, can differ
      accessToken: user.metadata.nukiAccessToken,
      refreshToken: user.metadata.nukiRefreshToken,
    }
  },
  onUpdateSession(context, auth) {
    const { userClaims } = context
    const { accessToken, refreshToken } = auth

    await gqlSDK.updateUserNukiAuth({
      id: userId,
      accessToken,
      refreshToken
    })
  }
})

export default server

OAuth 2.0 + ApiKeyToken creation

This is very similar to previous option with the different that after first login, an ApiKeyToken is automatically created and can be used instead of refreshing user session. ApiKeyToken is long-living token and does not require refresh or anything.

Go to Nuki Web and click on API on side navigation

Point OAuth2 Redirect URL to a Nhost serverless function:

https://local.functions.nhost.run/v1/callback/nuki

(Optional) Enable key creation via env

Api key creation can be enabled via environment variable or via code. If you like to use env variable follow the steps below, skip the section and check how to enable it via the function code.

  • Add NUKI_API_KEY_CREATE as an environment variable.

If you're using Nhost, add NUKI_API_KEY_CREATE to .env.development like this:

NUKI_API_KEY_CREATE=true

Create API token using the callback

Create a new Serverless Function: functions/callback/nuki.ts:

import { createNukiOAuthCallback } from '@dodi-smart/nuki-graphql-api'

const handler = (req, res) => {
  const {userId, apiKeyToken} = createNukiOAuthCallback(req, res, true)
  await gqlSDK.updateUserNukiAuth({
    id: userId,
    apiKey: apiKeyToken
  })
}

export default handler

Configure the Nuki GraphQL API

import { createNukiGraphQLServer } from '@dodi-smart/nuki-graphql-api'

const server = createNukiGraphQLServer({
  graphqlEndpoint: '/graphql/nuki',
  healthCheckEndpoint: '/graphql/nuki/health',
  provideAuth(context) {
    const { userClaims } = context

    const { user } = await gqlSDK.getUser({
      id: userId
    })

    return {
      // Example if nukiApiKey is saved in user's metadata, can differ
      accessToken: user.metadata.nukiApiKey,
    }
  },
  onUpdateSession(context, auth) {
    // Can be empty as API Keys are long-living tokens
  }
})

export default server

Permissions

Here's a minimal example without any custom permissions. Only requests using the x-hasura-admin-secret header will work:

import { createNukiGraphQLServer } from '@dodi-smart/nuki-graphql-api'

const server = createNukiGraphQLServer({
  graphqlEndpoint: '/graphql/nuki',
  healthCheckEndpoint: '/graphql/nuki/health',
  provideAuth(context) {
    /* ... */
  },
  onUpdateSession(context, auth) {
    /* ... */
  }
})

export default server

For more granular permissions, you can pass an isAllowed function to the createNukiGraphQLServer. The isAllowed function takes a context as parameter and runs every time the GraphQL server makes a request to Nuki to get or modify data for a specific Nuki user.

Here is an example of an isAllowed function:

import { createNukiGraphQLServer } from '@dodi-smart/nuki-graphql-api';

const isAllowed = (context: Context) => {
  const { isAdmin, userClaims } = context

  // allow all requests if they have a valid `x-hasura-admin-secret`
  if (isAdmin) {
    return true
  }

  // get user id
  const userId = userClaims['x-hasura-user-id']

  // check if the user is signed in
  if (!userId) {
    return false
  }

  // get more user information from the database
  const { user } = await gqlSDK.getUser({
    id: userId
  })

  if (!user) {
    return false
  }

  // other checks
}

const server = createNukiGraphQLServer({
  graphqlEndpoint: '/graphql/nuki',
  healthCheckEndpoint: '/graphql/nuki/health',
  isAllowed,
  provideAuth(context) {
    /* ... */
  },
  onUpdateSession(context, auth) {
    /* ... */
  }
});

export default server;

Context

The context object contains:

  • userClaims - verified JWT claims from the user's access token.
  • isAdmin - true if the request was made using a valid x-hasura-admin-secret header.
  • request - Fetch API Request object that represents the incoming HTTP request in platform-independent way. It can be useful for accessing headers to authenticate a user
  • query - the DocumentNode that was parsed from the GraphQL query string
  • operationName - the operation name selected from the incoming query
  • variables - the variables that were defined in the query
  • extensions - the extensions that were received from the client

Read more about the default context from GraphQL Yoga.