Quickstart
A step-by-step guide to the Navigraph SDK for TypeScript/JavaScript implementations of Navigraph APIs and services.
1. Setup project
First, scaffold a project in your favorite flashy framework. For this guide, we'll use Vite to quickly bootstrap a React project.
New Vite App
Create a new Vite app using your package manager of choice. In this guide, we'll use yarn as package manager and we'll pick the react-ts template from the available options.
npm create create vite navigraph-sdk-tutorial --template react-tspnpm create create vite navigraph-sdk-tutorial --template react-tsyarn create create vite navigraph-sdk-tutorial --template react-tsbun create create vite navigraph-sdk-tutorial --template react-tsThis will create a navigraph-sdk-tutorial directory. Switch to said directory:
cd navigraph-sdk-tutorial2. Install the Navigraph JS SDK
Install the navigraph package from NPM along with a library that can turn content into QR codes, such as qrcode.react.
npm install navigraph qrcode.reactpnpm add navigraph qrcode.reactyarn add navigraph qrcode.reactbun add navigraph qrcode.reactAs the SDK grows, you might want to only include the relevant parts of the SDK in your project. This can be done by manually installing the modules that make up the SDK.
The available modules are:
@navigraph/app
Used to set up the Navigraph SDK with your client-specific configuration. (required)
@navigraph/auth
Essentials needed to authenticate end-users (required)
@navigraph/charts
Fetch charts, georeferencing and airport information
@navigraph/amdb
Fetch geometries and graphs from the AMDB
@navigraph/weather
Fetch OPMET data such as METAR, TAF, Sigmets
@navigraph/packages
Fetch navigation data packages for use in aircraft instruments
3. Set up the SDK
Create an entrypoint for the Navigraph SDK in your application. This can be done by creating a lib/navigraph.ts file and filling it with the following:
import { initializeApp, Scope, NavigraphApp } from "navigraph/app";
const config: NavigraphApp = {
clientId: "<YOUR_NAVIGRAPH_CLIENT_ID>",
clientSecret: "<YOUR_NAVIGRAPH_CLIENT_SECRET>",
scopes: [Scope.CHARTS],
};
initializeApp(config);This will bootstrap the SDK with the necessary credentials and scopes needed for further interaction. However, before you can interact with any of Navigraph's APIs, you will need to sign in. The next step will be to set up the authentication module.
Configure authentication
The authentication module can be configured by calling getAuth(), as imported from navigraph/auth. Optionally, you can pass an object containing additional implementation details:
storage
The storage solution to use. Defaults to localStorage, which might not be preferable/available in your environment.
keys
The keys to use when storing the access/refresh tokens.
//...
export const auth = getAuth({
storage: {
// Optional
getItem: (key) => localStorage.getItem("NG" + key),
setItem: (key, value) => localStorage.setItem("NG" + key, value),
},
keys: {
// Optional
accessToken: "ACCESS_TOKEN",
refreshToken: "REFRESH_TOKEN",
},
});Configure API modules
Currently only the Charts API is exposed through the SDK. This module is very simple to set up:
//...
export const charts = getChartsAPI();4. Set up the UI
In order to start using the SDK, you'll need to let the user authenticate themselves.
To do this in React, we'll set up a useNavigraphAuth hook that utilizes the authentication module from earlier.
import { User } from "navigraph/auth";
import React, { useState, useEffect, useContext, createContext } from "react";
import { auth } from "../../src/lib/navigraph";
interface NavigraphAuthContext {
initialized: boolean;
user: User | null;
signIn: typeof auth.signInWithDeviceFlow;
}
const authContext = createContext<NavigraphAuthContext>({
initialized: false,
user: null,
signIn: () => Promise.reject("Not initialized"),
});
// Provider hook that creates auth object and handles state
function useProvideAuth() {
const [user, setUser] = useState<User | null>(null);
const [initialized, setinitialized] = useState(false);
// Subscribe to user on mount
// Because this sets state in the callback it will cause any
// component that utilizes this hook to re-render with the latest auth object.
useEffect(() => {
const unsubscribe = auth.onAuthStateChanged((u) => {
if (!initialized) setinitialized(true);
setUser(u);
});
// Cleanup subscription on unmount
return () => unsubscribe();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return {
user,
initialized,
signIn: auth.signInWithDeviceFlow,
};
}
// Provider component that wraps your app and makes auth object
// available to any child component that calls useAuth().
export function NavigraphAuthProvider({
children,
}: {
children: React.ReactNode;
}) {
const auth = useProvideAuth();
return <authContext.Provider value={auth}>{children}</authContext.Provider>;
}
// Hook for child components to get the auth object
// and re-render when it changes.
export const useNavigraphAuth = () => {
return useContext(authContext);
};In the above example, we're using a useEffect hook to subscribe to the onAuthStateChanged callback in the authentication module.
This callback fires whenever the module registers a change to the authenticated user.
When the page is first loaded, the authentication module will try to resume any previously persisted session. If it is unsuccessful,
the onAuthStateChanged callback will be fired with a value of null. If instead it is successful in restoring a session, the callback
will be fired with the details of the authenticated user.
The above stays true for when a sign-in process is completed using signInWithDeviceFlow().
In order to make use of this hook, we first need to wrap the application in the just created NavigraphAuthProvider component.
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
import "./index.css";
ReactDOM.createRoot(document.getElementById("root")!).render(
<React.StrictMode>
<NavigraphAuthProvider>
<App />
</NavigraphAuthProvider>
</React.StrictMode>
);Now we can go ahead and build a very simple UI that allows the user to sign in using the device flow.
By mounting the hook that we just created, we can now easily sign in by calling the signIn function that it returns.
This function takes a single argument, which is a callback that will be called once the sign-in process is started.
In it, you will receive all necessary information to display a link or QR code that the user can interact with to complete the authorization.
import React, { useState } from "react";
import QRCode from "qrcode.react";
import { DeviceFlowParams } from "navigraph/auth";
import { useNavigraphAuth } from "./hooks/useNavigraphAuth";
function App() {
const [params, setParams] = useState<DeviceFlowParams | null>(null);
const { user, signIn, initialized } = useNavigraphAuth();
const handleSignIn = () => signIn((p) => setParams(p));
return (
<main>
<h2>Navigraph SDK Demo</h2>
{!initialized && <div>Loading...</div>}
{initialized && !params && !user && (
<button onClick={handleSignIn}>Sign in</button>
)}
{params?.verification_uri_complete && !user && (
<>
<QRCode value={params.verification_uri_complete} size={250} />
<a
href={params.verification_uri_complete}
target="_blank"
rel="noreferrer"
>
Open sign in page
</a>
</>
)}
{user && (
<h2>
Welcome, <strong>{user.preferred_username}</strong>
</h2>
)}
</main>
);
}
export default App;Let's break the above down a little bit. First, we have a useNavigraphAuth hook that returns the following:
user: The authenticated user, if any.signIn: A function that starts the sign-in process.initialized: A boolean that indicates whether the authentication module has fully initialized.
Then, for the presentation, we begin by rendering a Loading... text if the authentication module is not yet initialized.
This should only be visible for a very short amount of time.
We then check if the authentication module has fully initialized. If it has, we check if there is an ongoing sign-in attempt
by looking at the params state. If no sign-in attempt is in progress, and no user is authenticated (as indicated by the user state),
we render a button that will start the sign-in process.
<>
{initialized && !params && !user && (
<button onClick={handleSignIn}>Sign in</button>
)}
</>In the next section, we handle the presentation of vital UI elements for the authentication process.
This is done by checking if the params state is set and that it contains a verification_uri_complete property.
If it does, the value of said key can be used to render a QR code or a direct link to the sign-in page.
<>
{params?.verification_uri_complete && !user && (
<>
<QRCode value={params.verification_uri_complete} size={250} />
<a href={params.verification_uri_complete} target="_blank">
Open sign in page
</a>
</>
)}
</>Finally, in the last section, we conditionally show a welcome message when a user has been authenticated.
As previously mentioned, this is done by verifying that the user state is set (non-null).
<>
{user && (
<h2>
Welcome, <strong>{user.preferred_username}</strong>
</h2>
)}
</>Next steps
For a more detailed specification of the SDK, see the API reference (coming soon). Also, feel free to check out an extended example over on CodeSandbox!