Skip to main content

Adding a custom address completion

The custom address completion solution from NewStore that uses address providers other than Apple Mapkit must be enabled first. See Enabling custom address completion .

Custom address completion using AWS lambda functions

You can leverage APIs from your preferred location or address providers. See the example below that demonstrates how custom address completion can be implemented via AWS Location Service.

Important

This is only an illustrative example of how a lambda function must be set up to support your preferred address or location provider.

Ensure that you work with your integration partner and NewStore to make modifications to the payload based on the provider you want to integrate with.

Setting up address completion via AWS Location Service

Illustrative example with AWS Location Service


import { ALBHandler, ALBEvent, ALBEventHeaders, ALBResult } from 'aws-lambda'
import countries from 'i18n-iso-countries'
import {
LocationClient,
SearchPlaceIndexForTextCommand,
} from '@aws-sdk/client-location'

interface FullAddress {
name: string
zip: string
city: string
cityDistrict: string
country: string
countryCode: string
administrativeArea: string
streetName: string
houseNumber: string
}

function getTenant(headers: ALBEventHeaders = {}): string | undefined {
try {
if (headers['X-Newstore-Tenant'] !== undefined) {
return headers['X-Newstore-Tenant']
}
if (headers['x-newstore-tenant'] !== undefined) {
return headers['x-newstore-tenant']
}
const authToken =
headers.Authorization?.split(' ')[1] ||
headers.authorization?.split(' ')[1] // for public urls
if (typeof authToken === 'string') {
const [, rawToken] = authToken.split('.')
const buff = Buffer.from(rawToken, 'base64')
const tokenData = buff.toString('utf-8')
const data = JSON.parse(tokenData)

if (data?.['http://newstore/tenant'] !== undefined) {
return data['http://newstore/tenant']
}
}
} catch (e) {
// handle error
}
}

function formatJsonResponse (statusCode: number, body?: unknown): ALBResult {
return {
statusCode,
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body),
}
}

const completeWithAWS = async (
query: string,
context: string,
locale = 'en-us',
): Promise<FullAddress | null> => {
const language = locale.split('-')[0]

const client = new LocationClient({
region: process.env.REGION ?? 'us-east-1',
})

const command = new SearchPlaceIndexForTextCommand({
IndexName: 'address-completion',
Text: query + ' ' + context,
FilterCategories: ['AddressType', 'StreetType'],
Language: language,
})

// Send the request to location or address provider client
const response = await client.send(command)

if (response.Results === undefined || response.Results.length === 0)
return null

const firstResult = response.Results[0]

if (firstResult.Place === undefined) return null

// Return the processed address completion results within this payload
return {
name: query,
zip: firstResult.Place.PostalCode ?? '',
city: firstResult.Place.Municipality ?? '',
cityDistrict: firstResult.Place.Neighborhood ?? '',
country:
(firstResult.Place.Country &&
countries.getName(firstResult.Place.Country, language, {
select: 'official',
})) ||
'',
countryCode:
(firstResult.Place.Country &&
countries.alpha3ToAlpha2(firstResult.Place.Country)) ||
'',
administrativeArea: firstResult.Place.Region ?? '',
streetName: firstResult.Place.Street ?? '',
houseNumber: firstResult.Place.AddressNumber ?? '',
}
}

export const handler: ALBHandler = async (event: ALBEvent) => {
const tenant = getTenant(event.headers)

if (typeof tenant !== 'string') {
return formatJsonResponse(400, { error: 'Missing tenant header.' })
}

try {
const query = event.queryStringParameters?.query
const context = event.queryStringParameters?.context
const language = event.queryStringParameters?.lang

if (typeof query !== 'string') {
return formatJsonResponse(400, { error: 'Missing query parameter.' })
}

if (typeof context !== 'string') {
return formatJsonResponse(400, { error: 'Missing context parameter.' })
}

const address = await completeWithAWS(query, context, language)
if (address === null) return formatJsonResponse(404)
return formatJsonResponse(200, address)
} catch (error) {
const message = error instanceof Error ? error.message : String(error)
return formatJsonResponse(400, { error: message })
}
}

Related topics