A simple weather application written in Nest.js.
Test it here.
Or see the OpenAPI documentation here.
This application serves as a weather forecasting service. It uses external APIs to locate the user by their IP address and then presents them with current weather condition in their area.
There are two endpoints available:
/v1/api/weather
/v1/api/weather/:city-name
The prior one will use user's local position,
the latter will serve weather forecast for a chosen city.
- It uses ipstack.com and ipgeolocation.io as the geolocation APIs
- It uses openweathermap.org as the weather API
- It uses Redis as the caching database
- After each request, the IP location, weather location and city location are saved in cache
- A single weather entry is valid in a 50 km radius from the assigned location
- IP location and weather location do expire after an hour, city location does not
$ npm install
To run the app you can use docker-compose file. First, you have to specify all the environmental variables in docker-compose.yaml and then run the command:
# running in docker
$ docker-compose -f ./docker-compose.yaml up
You can also run the app using Node.js locally:
# development
$ npm run start:dev
# production mode
$ npm run start:prod
The application takes some config values. They are read from a file .env.{STAGE}, where STAGE is a environmental variable, that has to be specified. The rest of the values are presented here:
Name | Description | Default value |
---|---|---|
STAGE | dev for development, prod for production | none (required) |
PORT | The port number of the application | 3000 |
GEOLOCATION_BASEURL | The base URL of the geolocation API | none (required) |
GEOLOCATION_ACCESS_KEY | The access key for the geolocation API | none (required) |
GEOLOCATION_BASEURL2 | The base URL of the second geolocation API | none |
GEOLOCATION_ACCESS_KEY2 | The access key for the second geolocation API | none |
WEATHER_BASEURL | The base URL of the weather API | none (required) |
WEATHER_ACCESS_KEY | The access key for the weather API | none (required) |
RETRIES | The maximal number of trials to establish connection to an API before returning an error | 5 |
BACKOFF | The initial back-off time between retries, in milliseconds (after each retry it is multiplied by 2) | 300 |
CACHE_USERNAME | The username for accessing the Redis database | none |
CACHE_PASSWORD | The password for accessing the Redis database | none |
CACHE_DATABASE_ADDRESS | The address of the Redis cache database | none (required) |
CACHE_DATABASE_PORT | The port of the Redis cache database | none (required) |
CACHE_IPADDRESSES_KEYNAME | The keyname of the Sorted Set in which the geolocation of IP addresses should be kept | IPAddresses |
CACHE_IPEXP_KEYNAME | The keyname of the IP addresses expiration Sorted Set | IPExp |
CACHE_WEATHERID_KEYNAME | The keyname of the Sorted Set in which the weather IDs should be kept | WeatherID |
CACHE_WEATHERDATA_KEYNAME | The keyname of the Hash in which the weather JSON data should be kept | WeatherData |
CACHE_WEATHEREXP_KEYNAME | The keyname of the weather entries expiration Sorted Set | WeatherExp |
CACHE_CITIES_KEYNAME | The keyname of the city geolocation Sorted Set | Cities |
CACHE_WEATHER_RADIUS | The radius of the cached weather in kilometers | 50 |
CACHE_IP_TTL | The IP addresses' geolocation's Time To Live in milliseconds | 3600000 |
CACHE_WEATHER_TTL | The weather's geolocation's Time To Live in milliseconds | 3600000 |
CORS_ORIGIN | The allowed origin for Cross-Origin-Resource-Sharing | http://localhost:3001 |
THROTTLER_TTL | The time in seconds for which each request will last in storage | 60 |
THROTTLER_LIMIT | The maximum number of requests that can be made within the limit | 10 |
I only wrote unit tests for this application.
$ npm run test
Due to the fact that my skills are still limited, the solutions I used could be most probably greatly improved (most noticeably cache management):
- The cache expiration should be ideally handled by the cache database itself (not possible with Redis)
- All the queries (including wrong city names) could be saved in cache, at least for short period of time. TypeORM can do that efficiently (link)
- There should be more tests
- The geolocation RequestObject should be more flexible (it should be easier to add a new API)
- For a bigger number of APIs, maybe an API hub like rapidapi.com should be used (?)
- Cache should be ideally managed in interceptors
- Global validation pipes could have been used for checking some arguments