{"id":13798634,"url":"https://github.com/sentinel-hub/sentinelhub-js","last_synced_at":"2026-04-02T23:50:20.991Z","repository":{"id":37824008,"uuid":"235751303","full_name":"sentinel-hub/sentinelhub-js","owner":"sentinel-hub","description":"Download and process satellite imagery in JavaScript or TypeScript using Sentinel Hub services.","archived":false,"fork":false,"pushed_at":"2025-04-01T08:08:33.000Z","size":3224,"stargazers_count":55,"open_issues_count":21,"forks_count":8,"subscribers_count":6,"default_branch":"master","last_synced_at":"2025-04-09T22:17:00.734Z","etag":null,"topics":["sentinel","sentinel-1","sentinel-2","sentinel-3","sentinel-5p","sentinel-hub"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sentinel-hub.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-01-23T08:16:01.000Z","updated_at":"2025-04-01T08:07:56.000Z","dependencies_parsed_at":"2023-12-18T15:13:57.801Z","dependency_job_id":"5ad5c6d7-bbd3-4e0d-b110-b7b2f7369b40","html_url":"https://github.com/sentinel-hub/sentinelhub-js","commit_stats":{"total_commits":2242,"total_committers":13,"mean_commits":"172.46153846153845","dds":0.7756467439785906,"last_synced_commit":"4a38647785412e7b49ba007248441a496c12ec5b"},"previous_names":[],"tags_count":411,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sentinel-hub%2Fsentinelhub-js","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sentinel-hub%2Fsentinelhub-js/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sentinel-hub%2Fsentinelhub-js/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sentinel-hub%2Fsentinelhub-js/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sentinel-hub","download_url":"https://codeload.github.com/sentinel-hub/sentinelhub-js/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248119287,"owners_count":21050755,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["sentinel","sentinel-1","sentinel-2","sentinel-3","sentinel-5p","sentinel-hub"],"created_at":"2024-08-04T00:00:47.933Z","updated_at":"2026-02-04T10:52:06.169Z","avatar_url":"https://github.com/sentinel-hub.png","language":"TypeScript","funding_links":[],"categories":["👨‍💻 JavaScript Libraries","Languages other than `Python` and `R`"],"sub_categories":["Remote Sensing","Testing your code"],"readme":"\n- [Installation](#installation)\n- [Usage](#usage)\n  - [Layers](#layers)\n  - [Fetching images](#fetching-images)\n    - [Effects](#effects)\n  - [Searching for data](#searching-for-data)\n  - [Requests configuration](#requests-configuration)\n  - [Getting basic statistics and histogram](#getting-basic-statistics-and-histogram)\n  - [Backwards compatibility](#backwards-compatibility)\n  - [Authentication for Processing API](#authentication-for-processing-api)\n  - [Debugging](#debugging)\n- [Examples](#examples)\n  - [Preparation before running examples](#preparation-before-running-examples)\n  - [Running examples](#running-examples)\n    - [Node.js](#node.js)\n    - [Storybook](#storybook)\n- [Copyright and license](#copyright-and-license)\n\n-----\n\n# Installation\n\n```\n$ npm install @sentinel-hub/sentinelhub-js\n```\n\n# Usage\n\n## Layers\n\nThe core data structure is `Layer`, which corresponds to a _layer_ as returned by OGC WMS GetCapabilities request. Basic (WMS-capable) `Layer` can be initialized like this:\n\n```javascript\n  import { WmsLayer } from '@sentinel-hub/sentinelhub-js';\n\n  const layer = new WmsLayer({\n    baseUrl: 'https://services.sentinel-hub.com/ogc/wms/\u003cyour-instance-id\u003e',\n    layerId: '\u003clayer-id\u003e',\n  });\n```\n\nSuch layer would only allow WMS requests. However, `Layer` is also a superclass for multiple dataset-specific subclasses (like `S1GRDAWSEULayer` - Sentinel-1 GRD data on AWS eu-central-1 Sentinel Hub endpoint) which can be instantiated with their own specific parameters and thus have additional capabilities.\n\nWhen it comes to Sentinel Hub layers, there are four ways to determine their content:\n\n- by `instanceId` and `layerId`: ID of the layer, as used by OGC WMS and SentinelHub Configurator\n- by `evalscript`: custom (javascript) code that will be executed by the service per each pixel and will calculate the (usually RGB/RGBA) values\n- by `evalscriptUrl`: the URL from which the evalscript can be downloaded from\n- by `dataProduct`: the ID of a pre-existing data product\n\n```javascript\n  import { S1GRDAWSEULayer } from '@sentinel-hub/sentinelhub-js';\n\n  let layerS1;\n  layerS1 = new S1GRDAWSEULayer({\n    instanceId: '\u003cmy-instance-id\u003e',\n    layerId: '\u003clayer-id\u003e',\n  );\n  layerS1 = new S1GRDAWSEULayer({\n    evalscript: myEvalscript,\n    title: 'Title',\n    description: 'Description',\n    acquisitionMode: AcquisitionMode.IW,\n    polarization: Polarization.DV,\n    resolution: Resolution.HIGH,\n  });\n  layerS1 = new S1GRDAWSEULayer({\n    evalscriptUrl: myEvalscriptUrl,\n    acquisitionMode: AcquisitionMode.IW,\n    polarization: Polarization.DV,\n    resolution: Resolution.HIGH,\n  });\n  layerS1 = new S1GRDAWSEULayer({\n    dataProduct: '\u003cdata-product-id\u003e',\n    acquisitionMode: AcquisitionMode.EW,\n    polarization: Polarization.DH,\n    resolution: Resolution.MEDIUM,\n  });\n```\n\nIt is also possible to create layers by importing their definitions from the Sentinel Hub configuration instance:\n\n```javascript\n  import { LayersFactory } from '@sentinel-hub/sentinelhub-js';\n\n  const layers = await LayersFactory.makeLayers('https://services.sentinel-hub.com/ogc/wms/\u003cyour-instance-id\u003e');\n    // [ layer1, layer2, ... ] - a list of Layer objects\n\n  const layersIds = layers.map(l =\u003e l.layerId);\n    // [ '\u003clayer-id-1\u003e', '\u003clayer-id-2\u003e',... ]\n```\n\nDepending on the first parameter (`baseUrl`), method `makeLayers()` tries to determine if a specific `Layer` subclass would be better suited and instantiates it with all applicable parameters.\n\nThe list can be filtered to include only some of the layers:\n```javascript\n  import { LayersFactory, DATASET_S2L2A } from '@sentinel-hub/sentinelhub-js';\n\n  // this will return only a list of those S2L2A layers whose IDs start with \"ABC_\":\n  const layers = await LayersFactory.makeLayers(\n    'https://services.sentinel-hub.com/ogc/wms/\u003cyour-instance-id\u003e',\n    (layerId, dataset) =\u003e layerId.startsWith(\"ABC_\") \u0026\u0026 dataset === DATASET_S2L2A,\n  );\n```\n\nAlternatively, we can also fetch a single layer by using `makeLayer` method:\n```javascript\n  import { LayersFactory } from '@sentinel-hub/sentinelhub-js';\n\n  const layer = await LayersFactory.makeLayer('https://services.sentinel-hub.com/ogc/wms/\u003cyour-instance-id\u003e', '\u003clayer-id\u003e');\n```\n\nSome additional layer information can be passed to `makeLayer` and `makeLayers` as an object in order to create layers with the provided information instead of the information from the services.\n\n```javascript\n  const layer = await LayersFactory.makeLayer('https://services.sentinel-hub.com/ogc/wms/\u003cyour-instance-id\u003e', '\u003clayer-id\u003e', { maxCloudCoverPercent: 30 });\n\n  const layers = await LayersFactory.makeLayers('https://services.sentinel-hub.com/ogc/wms/\u003cyour-instance-id\u003e', null, { maxCloudCoverPercent: 30 });\n  ```\n\nSome information about the layer is only accessible to authenticated users. The process of getting the authentication token and authenticating is described in [Authentication for Processing API](#authentication-for-processing-api).\n\n## Fetching images\n\nMaps which correspond to these layers can be fetched via different protocols like WMS and Processing. Not all of the protocols can be used in all cases; for example, Processing can only render layers for which it has `evalscript` available and for which evalscript version 3 is used.\n\n```javascript\n  import { BBox, CRS_EPSG4326, MimeTypes, ApiType } from '@sentinel-hub/sentinelhub-js';\n\n  const bbox = new BBox(CRS_EPSG4326, 18, 20, 20, 22);\n  const getMapParams = {\n    bbox: bbox,\n    fromTime: new Date(Date.UTC(2018, 11 - 1, 22, 0, 0, 0)),\n    toTime: new Date(Date.UTC(2018, 12 - 1, 22, 23, 59, 59)),\n    width: 512,\n    height: 512,\n    format: MimeTypes.JPEG,\n  };\n\n  const imageBlob = await layer.getMap(getMapParams, ApiType.WMS);\n  const imageBlob2 = await layer.getMap(getMapParams, ApiType.PROCESSING);\n```\n\nNote that both of the images above should be _exactly_ the same.\n\nIn some cases we can retrieve just the image URL instead of a blob:\n\n```javascript\n  const imageUrl = layer.getMapUrl(getMapParams, ApiType.WMS);\n  const imageUrl2 = layer.getMapUrl(getMapParams, ApiType.PROCESSING); // exception thrown - Processing API does not support HTTP GET method\n```\n\nIt is also possible to determine whether a layer supports a specific ApiType:\n```javascript\n  if (layer.supportsApiType(ApiType.PROCESSING)) {\n    imageUrl = layer.getMapUrl(getMapParams, ApiType.PROCESSING);\n  } else {\n    imageUrl = layer.getMapUrl(getMapParams, ApiType.WMS);\n  };\n```\n\nIf your evalscript contains multiple [output response objects](https://docs.sentinel-hub.com/api/latest/evalscript/v3/#output-object-properties), you can set the `outputResponseId` to set which output should be returned.\n\n**Note:** This feature is only available with Processing API.\n\n```javascript\n  const getMapParams = {\n    bbox: bbox,\n    fromTime: new Date(Date.UTC(2018, 11 - 1, 22, 0, 0, 0)),\n    toTime: new Date(Date.UTC(2018, 12 - 1, 22, 23, 59, 59)),\n    width: 512,\n    height: 512,\n    format: MimeTypes.JPEG,\n    outputResponseId: 'default',\n  };\n\n  const imageBlob = await layer.getMap(getMapParams, ApiType.PROCESSING);\n```\n\n### Optimizing the data retrieval\n\nThis library is often used to display satellite imagery on the map. Data in this case is requested in \"tiles\" (typically of 256x256 or 512x512 pixels) and is often overlaid over some background map, which shows land cover, borders, roads, places,... Thus, when making `getMap` requests, it is usually desirable to get images which are transparent in places where the satellite data is not available. The easiest solution is to use `PNG` format instead of `JPEG` (because `JPEG` does not support transparency), however this makes the size of the images much bigger, leading to longer load times on slow connections.\n\nTo solve this issue, there is a special format available (`MimeTypes.JPEG_OR_PNG`). If specified, `getMap` call will try to determine if it should use `JPEG` or `PNG` based on the data available. If requested bounding box is fully covered with data, it will use JPEG (for performance reasons), otherwise it will use PNG and will return an image with transparent channel.\n\nCAREFUL: this setting should only be used if the retrieved data is not transparent (within the tiles). In other words: if `evalscript` returns a transparent image channel, using `PNG` is probably the only viable option.\n\n```javascript\n  const getMapParams = {\n    bbox: new BBox(CRS_EPSG4326, 18.3, 20.1, 18.7, 20.4),\n    fromTime: new Date(Date.UTC(2018, 11 - 1, 22, 0, 0, 0)),\n    toTime: new Date(Date.UTC(2018, 12 - 1, 22, 23, 59, 59)),\n    width: 512,\n    height: 512,\n    format: MimeTypes.JPEG_OR_PNG,\n  };\n  const imageBlob = await layer.getMap(getMapParams, ApiType.WMS);\n  const imageBlob2 = await layer.getMap(getMapParams, ApiType.PROCESSING);\n```\n\n### Effects\n\nWhen requesting an image, effects can be applied to visually improve the image.\nTo apply the effects, the `effects` param in `getMapParams` should be present, containing the desired effects.\nSupported effects are `gain`, `gamma`, `redRange`, `greenRange`, `blueRange` and `customEffect`.\n\nEffects `gain` and `gamma` accept values equal or greater than 0.\n\nEffects `redRange`, `greenRange` and `blueRange` accept the values between 0 and 1, including both 0 and 1.\nSetting values to `redRange`, `greenRange` and `blueRange` limits the values that pixels can have for red, green and blue color component respectively.\n\nEffect `customEffect` is a function that receives red, green and blue values and returns new red, green and blue values.\nIt operates with values between 0 and 1, including both 0 and 1.\n\n```javascript\n  const getMapParamsWithEffects = {\n    bbox: bbox,\n    fromTime: new Date(Date.UTC(2018, 11 - 1, 22, 0, 0, 0)),\n    toTime: new Date(Date.UTC(2018, 12 - 1, 22, 23, 59, 59)),\n    width: 512,\n    height: 512,\n    format: MimeTypes.JPEG,\n    effects: {\n      gain: 1.2,\n      gamma: 0.9,\n      redRange: {from: 0.2, to: 0.8},\n      greenRange: {from: 0.2, to: 0.8},\n      blueRange: {from: 0.2, to: 0.8},\n      customEffect: ({r,g,b,a}) =\u003e ({r,g,b,a})\n      }\n    }\n  };\n\n  const imageBlob = await layer.getMap(getMapParamsWithEffects, ApiType.WMS);\n  const imageBlob2 = await layer.getMap(getMapParamsWithEffects, ApiType.PROCESSING);\n```\n\n**Note:** Effects are applied by the library (client-side) and are thus only available when the blob is retrieved (`getMap`) and not through the URL (`getMapUrl`).\nWhen retrieving an image URL (via `getMapUrl()`) with effects in the parameters, an error is thrown, because the retrieved URL points directly to the image on the services with no applied effects.\n\n### Stitching images\n\nServices limit the size of the output image per request (2500px in each direction). If we need a bigger image, we can issue multiple requests and stitch the results together in a canvas. A utility method `getHugeMap` allows us to do that seamlessly.\n\nIMPORTANT: be careful with the image sizes as a big image could consume a lot of processing units. There is no limit imposed by this method.\n\n```javascript\n  const imageBlob = await layer.getHugeMap(getMapParams, ApiType.PROCESSING, requestsConfig);\n```\n\n## Searching for data\n\nSearching for the data is a domain either of a _layer_ or its _dataset_ (if available). This library supports different services, some of which (ProbaV and GIBS for example) specify availability dates _per layer_ and not dataset.\n\nWe can always use layer to search for data availability:\n```typescript\n  import { OrbitDirection } from '@sentinel-hub/sentinelhub-js';\n\n  const layerS2L2A = new S2L2ALayer({\n    instanceId: '\u003cmy-instance-id\u003e',\n    layerId: '\u003clayer-id-S2L2A\u003e',\n    maxCloudCoverPercent: 50,\n  });\n  const { tiles, hasMore } = await layerS2L2A.findTiles(bbox, fromTime, toTime, maxCount, offset);\n  const flyoversS2L2A = await layerS2L2A.findFlyovers(bbox, fromTime, toTime);\n  const datesS2L2A = await layerS2L2A.findDatesUTC(bbox, fromTime, toTime);\n\n  const layerS1 = new S1GRDAWSEULayer({\n    instanceId: '\u003cmy-instance-id\u003e',\n    layerId: '\u003clayer-id-S1GRD\u003e',\n    orthorectify: true,\n    backscatterCoeff: BackscatterCoeff.GAMMA0_ELLIPSOID,\n    orbitDirection: OrbitDirection.ASCENDING,\n  });\n  const { tiles: tilesS1 } = await layerS1.findTiles(bbox, fromTime, toTime, maxCount, offset);\n  const flyoversS1 = await layerS1.findFlyovers(bbox, fromTime, toTime);\n  const datesS1 = await layerS1.findDatesUTC(bbox, fromTime, toTime);\n```\n\n\n## Requests configuration\n\nYou can specify that network requests should be retried by passing the max. number of retries for each of the network requests used by the method. If not specified or set to `null`, the default value for `retries` is used (`2` - which means 3 attempts altogether). To disable retrying, set it to `0`.\n\n```typescript\nconst requestsConfig = {\n  retries: 1, // max. 2 attempts for each of the network requests within the called method\n};\n```\n\nYou can specify a timeout in milliseconds for network requests. This will cancel all the network requests triggered by the method after the specified time frame. Default value for `timeout` is `null` (disabled).\n\nSpecifying the timeout will limit the time spent in the method, by cancelling the network requests (including retries) that take too long.\n\n```typescript\nimport { isCancelled } from '@sentinel-hub/sentinelhub-js';\n\nconst requestsConfig = {\n  timeout: 5000,\n};\n\ntry {\n  const img = await layer.getMap(getMapParams, ApiType.PROCESSING, requestsConfig);\n  const dates = await layer.findDatesUTC(bbox, fromTime, toTime, requestsConfig);\n  const stats = await layer.getStats(getStatsParams, requestsConfig);\n  const tiles = await layer.findTiles(bbox, fromTime, toTime, null, null, requestsConfig);\n} catch (err) {\n  // The exception thrown by canceling network requests can be caught and identified by `isCancelled`.\n  if (!isCancelled(err)) {\n    throw err;\n  }\n}\n```\n\nYou can also cancel requests explicitly when searching/fetching data. To do so a token needs to be created and passed through the requests configuration object.\n\nIn the example below, a cancel token is passed inside the configuration request object. The timeout will cancel the requests after 500 miliseconds, throwing an exception.\n\n```typescript\nimport { CancelToken, isCancelled } from '@sentinel-hub/sentinelhub-js';\n\nconst token = new CancelToken();\n\nconst requestsConfig = {\n  cancelToken: token,\n  retries: 4,\n};\n\nconst requestTimeout = setTimeout(() =\u003e {\n  token.cancel();\n}, 500);\n\ntry {\n  const img = await layer.getMap(getMapParams, ApiType.PROCESSING, requestsConfig);\n  const dates = await layer.findDatesUTC(bbox, fromTime, toTime, requestsConfig);\n  const stats = await layer.getStats(getStatsParams, requestsConfig);\n  const tiles = await layer.findTiles(bbox, fromTime, toTime, null, null, requestsConfig);\n  clearTimeout(requestTimeout);\n} catch (err) {\n  // The exception thrown by canceling network requests can be caught and identified by `isCancelled`.\n  if (!isCancelled(err)) {\n    throw err;\n  }\n}\n```\n\nCaching is enabled by default where items expire in 30 minutes. Expired items are deleted every minute.  \nTo modify the caching, one can add `expiresIn` to the requests configuration object. The values are in seconds. Value `0` disables caching.\n```javascript\n// cache is valid for 30 minutes:\nconst requestsConfig = {\n  cache: {\n    expiresIn: 1800,\n  }\n};\n```\n\nResponses can be cached to [Cache API](https://developer.mozilla.org/en-US/docs/Web/API/Cache):\n```javascript\n  const requestsConfig = {\n    cache: {\n      expiresIn: 5000,\n      targets: [CacheTarget.CACHE_API],\n    },\n  };\n```\n\nThey can also be cached to memory:\n```javascript\n  const requestsConfig = {\n    cache: {\n      expiresIn: 5000,\n      targets: [CacheTarget.MEMORY],\n    },\n  };\n```\n\nA list of targets can be provided which is ordered by priority, and the first available target in the list will be used. This example will fallback to caching to memory if [CACHE_API](https://developer.mozilla.org/en-US/docs/Web/API/Cache) is not available:\n\n```javascript\n  const requestsConfig = {\n    cache: {\n      expiresIn: 5000,\n      targets: [CacheTarget.CACHE_API, CacheTarget.MEMORY],\n    },\n  };\n```\n\nIf a default requests configuration object is specified, it will be used for any key which is not set explicitly:\n\n```javascript\n  setDefaultRequestsConfig({\n    retries: 2,\n  });\n```\n\n## Getting basic statistics and histogram\n\nGetting basic statistics (mean, min, max, standard deviation) and a histogram for a geometry (Polygon or MultiPolygon).\nThe histogram uses the `equalfrequency` binning method and defaults to 5 bins.\n\n```javascript\n  const stats = await layer.getStats({\n    geometry: bbox.toGeoJSON(),\n    fromTime: new Date(Date.UTC(2018, 11 - 1, 22, 0, 0, 0)),\n    toTime: new Date(Date.UTC(2018, 12 - 1, 22, 23, 59, 59)),\n    resolution: resolution,\n    bins: 10,\n  });\n```\n\n\n## Backwards compatibility\n\nTo make it easier to use this library with legacy code, there are two functions that are implemented on top of the library, which do not require instantiating a `Layer` subclass.\n\n```javascript\n  const imageBlob1 = await legacyGetMapFromParams(rootUrl, wmsParams);\n  const imageBlob2 = await legacyGetMapFromParams(rootUrl, wmsParams, ApiType.PROCESSING); // ApiType.WMS is default\n```\n\nIf we already have a WMS GetMap URL, we can use it directly:\n\n```javascript\n  const imageBlob3 = await legacyGetMapFromUrl(fullUrlWithWmsQueryString);\n  const imageBlob4 = await legacyGetMapFromUrl(fullUrlWithWmsQueryString, ApiType.PROCESSING);\n```\n\n`legacyGetMapFromParams` and `legacyGetMapFromUrl` accept all parameters that are supported in [OGC WMS GetMap standard](https://www.sentinel-hub.com/develop/api/ogc/standard-parameters/wms/) and [Sentinel hub OGC API](https://www.sentinel-hub.com/develop/api/ogc/custom-parameters/), either as a property inside `wmsParams` object or as a substring of the `fullUrlWithWmsQueryString`.\nExample params: `gain`, `gamma`, `upsampling`, `downsampling`, etc.\n\n`legacyGetMapFromParams` and `legacyGetMapFromUrl` also accept the parameters that are used for creating a dataset-specific layer object or for getting the data with `getMap()` function but are not supported in [OGC WMS GetMap standard](https://www.sentinel-hub.com/develop/api/ogc/standard-parameters/wms/) and [Sentinel hub OGC API](https://www.sentinel-hub.com/develop/api/ogc/custom-parameters/).\n- Parameters which would be used for creating a `Layer` can be passed inside of `overrideLayerConstructorParams`.\nExample params: dataset-specific params for creating [layers](#layers)\n- Parameters which would be passed to `getMap` can be passed inside the `overrideGetMapParams`.\nExample params: [effects](#effects)\n\n```javascript\n  const imageBlob5 = await legacyGetMapFromParams(\n    rootUrl,\n    wmsParams,\n    ApiType.PROCESSING\n    fallbackToWmsApi,\n    overrideLayerConstructorParams,\n    overrideGetMapParams,\n  );\n```\n\n## Authentication for Processing API\n\nRequests to Processing API need to be authenticated.\nDocumentation about authentication is available at [Sentinel Hub documentation](https://docs.sentinel-hub.com/api/latest/#/API/authentication).\n\nIn short, authentication is done by getting an authentication token using OAuth Client's id and secret, and setting it.\n\nTo get the OAuth Client's id and secret, a new OAuth Client must be created in [**User settings**](https://apps.sentinel-hub.com/dashboard/#/account/settings) on **Sentinel Hub Dashboard** under **OAuth clients**.\nOAuth Client's secret is shown only before the creation process is finished so be mindful to save it.\n\nGetting the authentication token by calling `requestAuthToken()` with the OAuth Client's id and secret as its parameters and then setting the authentication token:\n\n```javascript\n  import { setAuthToken, requestAuthToken } from '@sentinel-hub/sentinelhub-js';\n\n  const clientId = /* OAuth Client's id, best to put it in .env file and use it from there */;\n  const clientSecret = /* OAuth client's secret, best to put it in .env file and use it from there */;\n  const authToken = await requestAuthToken(clientId, clientSecret);\n\n  const before = isAuthTokenSet(); // false\n  setAuthToken(authToken);\n  const after = isAuthTokenSet(); // true\n```\n\nAlternatively, authentication token can be set on a per-request basis, which also overrides any global token that was set by `setAuthToken`:\n\n```javascript\n  const requestsConfig = {\n    authToken: authToken,\n  };\n  const img = await layer.getMap(getMapParams, ApiType.PROCESSING, requestsConfig);\n```\n\n## Utility functions\n\n### Async conversion between Blob and Canvas\n\nFunction `drawBlobOnCanvas` allows drawing a `Blob` on existing canvas element:\n\n```javascript\n  const blob = await layer.getMap(params, ApiType.WMS);\n\n  const canvas = document.createElement('canvas');\n  canvas.width = params.width;\n  canvas.height = params.height;\n  const ctx = canvas.getContext('2d');\n\n  await drawBlobOnCanvas(ctx, blob, 0, 0);\n```\n\nFunction `canvasToBlob` converts the provided canvas to `Blob`:\n\n```javascript\n  const blob = await canvasToBlob(canvas);\n```\n\n## Debugging\n\nThis library is an abstraction layer that provides nice interface for accessing the underlying services, which simplifies development - but when requests fail, it is sometimes difficult to understand why. To enable easier debugging, `setDebugEnabled` can be used:\n\n```javascript\n  import { setDebugEnabled } from '@sentinel-hub/sentinelhub-js';\n\n  setDebugEnabled(true);\n  // ... failing operation\n  setDebugEnabled(false);\n```\n\nWhile debug mode is enabled, library will output any request it makes (even if the response comes from cache) to console in the form of a `curl` command.\n\n# Examples\nThis project contains some examples to demonstrate how the library is used.\nSome preparation is needed before running the examples.\n\n## Preparation before running examples\nTo run the examples, the environment variables must be set.\nThese variables should be put in the `.env` file in the root folder of this project.\n\n- `CLIENT_ID`: OAuth Client's id (optional, authentication is needed for examples that use Processing API)\n- `CLIENT_SECRET`: OAuth Client's secret (optional, authentication is needed for examples that use Processing API)\n\n- `INSTANCE_ID`: id of the configuration instance that will be used in examples\n- `S1GRDIW_LAYER_ID`: id of the Sentinel-1 GRD IW layer from that instance\n- `S1GRDEW_LAYER_ID`: id of the Sentinel-1 GRD EW layer from that instance\n- `S2L2A_LAYER_ID`: id of the Sentinel-2 L2A layer from that instance\n- ... (see `.env.example` for full list)\n\nInstance can be created with the [**Configurator**](https://apps.sentinel-hub.com/dashboard/#/configurations) on the **Sentinel Hub Dashboard**. It should contain the layers in the list above for examples to work.\n\n`CLIENT_ID` and `CLIENT_SECRET` are needed so that the authentication token can be requested, which is then used in examples that use Processing API.\nThe process of getting those two is described in [Authentication for Processing API](#authentication-for-processing-api)\n\n## Running examples\n\n### Node.js\n\n```bash\n$ npm install\n$ npm run build\n$ cd example/node\n$ node index.js\n```\n\n### Storybook\n\n```bash\n$ npm install\n$ npm run build\n$ cp .env.example .env\n  (... edit .env ...)\n$ npm run storybook\n```\n\n# Copyright and license\n\nCopyright (c) 2020 Sinergise Ltd. Code released under the [MIT License](https://github.com/sentinel-hub/sentinelhub-js/blob/master/LICENSE.md).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsentinel-hub%2Fsentinelhub-js","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsentinel-hub%2Fsentinelhub-js","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsentinel-hub%2Fsentinelhub-js/lists"}