DEV Community

Cover image for Handling “Adjacent Goil” addresses: building deliveries in Ghana with TypeScript
Dominic
Dominic

Posted on

Handling “Adjacent Goil” addresses: building deliveries in Ghana with TypeScript

In Ghana, "Adjacent Goil Filling Station, Madina Zongo Junction" is a perfectly normal address. Riders call customers three times to find the gate, cash on delivery goes unreconciled, and nobody can prove the parcel reached the right place.

I've been building AfriHex to fix that whole chain, and today the official TypeScript SDK is on npm. Here's a tour of what you can build with it.

npm install @afrihex/sdk
Enter fullscreen mode Exit fullscreen mode

It's typed end to end, has zero dependencies, and runs in Node.js 18+ and modern browsers.

1. Get a key, then a test key

Sign up for a free key with one request (no password, no card):

curl -X POST "https://api.afrihex.com/v2/self/signup" \
  -H "Content-Type: application/json" \
  -d '{"name": "Your App", "email": "you@example.com"}'
Enter fullscreen mode Exit fullscreen mode

Then create a test key so your experiments don't touch real customers:

import { AfriHex } from '@afrihex/sdk'

const afrihex = new AfriHex({ apiKey: process.env.AFRIHEX_API_KEY! })

const { key } = await afrihex.account.createTestKey() // afx_test_…, shown once
const test = new AfriHex({ apiKey: key })
Enter fullscreen mode Exit fullscreen mode

Test calls don't use your quota, keep their data separate from live data, and never send real SMS.

2. Parse addresses the way people actually write them

parse understands "adjacent", "opposite", "behind", "beside", "near", "next to" and "after":

const { parsed } = await afrihex.addresses.parse(
  'Adjacent Goil Filling Station, Madina Zongo Junction'
)

parsed?.anchors?.[0]   // { relation: 'adjacent', name: 'goil filling station' }
parsed?.geocode_query  // 'madina zongo junction'

// Find the landmark itself, anchored to the place
const goil = await afrihex.landmarks.geocode({
  q: 'goil',
  anchor: 'Madina Zongo Junction',
  kind: 'fuel_station',
})
goil.matches[0] // Goil, 276 m from the junction
Enter fullscreen mode Exit fullscreen mode

Reverse geocoding works the other way, describing a point by a landmark a rider will recognise:

const here = await afrihex.addresses.reverse(5.6319, -0.14, { descriptors: true })
here.descriptor?.text // "Opposite Nick Petroleum filling station, Gbatsuna"
Enter fullscreen mode Exit fullscreen mode

3. Route a rider

const route = await afrihex.routing.route({
  from: { point: { lat: 5.6457, lng: -0.075 } },
  to: { gps_code: 'GA-183-8164' },
  mode: 'motor_scooter',
})
Enter fullscreen mode Exit fullscreen mode

For a fleet, routing.plan assigns jobs across riders, including several trips per rider.

4. Track the delivery and reconcile the cash

const trip = await afrihex.trips.create({
  destination: { gps_code: 'GA-183-8164' },
  rider_ref: 'kofi',
  amount_to_collect: 120,
  recipient_phone: '0241234567',
})

trip.tracking_url // send this to the customer

await afrihex.trips.ping(trip.id, { lat: 5.64, lng: -0.10 })
await afrihex.trips.recordAttempt(trip.id, { reason: 'customer_unreachable', next_action: 'retry' })
await afrihex.trips.recordCollection(trip.id, { amount: 120, method: 'cash' })
await afrihex.trips.update(trip.id, { status: 'completed' })

const balances = await afrihex.cash.balances() // collected, handed in, still owed per rider
Enter fullscreen mode Exit fullscreen mode

You can also quote prices with delivery.quote and issue proof of delivery at the gate with pod.issue and pod.confirm.

5. Stable location codes, even offline

Every location gets an AfriHex code, built on Uber's open H3 grid:

const hex = await afrihex.hex.encode(5.6221843, -0.1729361, 7)
hex.code       // "AF-GH-7-0GXTJJBDZZZZZ"  (store this)
hex.short_code // "AF-GH-7-0GXTJJBD"       (show this)
Enter fullscreen mode Exit fullscreen mode

Rider on a road with no signal? The companion package @afrihex/offline creates and reads codes on the device with no internet.

Errors are typed

import { AfriHexError } from '@afrihex/sdk'

try {
  await afrihex.trips.get('unknown-id')
} catch (err) {
  if (err instanceof AfriHexError) console.error(err.code, err.status) // TRIP_NOT_FOUND, 404
}
Enter fullscreen mode Exit fullscreen mode

A note on keys

Keep your API key on your server. For mobile apps, mint a per-user token instead of shipping your developer key in the app. The Mobile Integration guide shows the pattern.

Try it

Trips, cash, fleet planning and pricing need a paid plan; addresses, routing and hex codes work on the free tier.

If you're building anything with deliveries or addresses in Ghana, I'd love to hear what you're working on. Drop a comment, or tell me which SDK you'd want next.

Top comments (0)

⌂ 换网址