cgapp logo
Historical Exchange Rate Rest API Using Golang

An API server using backend (Golang) and database (Postgres) containerised with (Docker)!

go version REST API 

test status code coverage

## 📖 Problem

Build an API server which load historical rate data from ecb.europa.eu and develop 3 endpoint

⚡️ Quick start

Run test 
     $ go test ./...  # run from root of the project directory
Using docker 
     $ docker-compose up --build --force-recreate # run it from root of the project directory

🔔 NoteIf you interested to run it from locally without Docker please ensure postgres database is up running also env is properly configured. Then create database table using /build/init.sql file.

  • go run /cmd/app/main.go

Example .env file

COMPOSE_FILE=build/docker-compose.yml
POSTGRES_USER=postgres
POSTGRES_PASSWORD=postgres
POSTGRES_HOST=database
POSTGRES_PORT=5432
DATABASE_NAME=historical_rate
SERVER_PORT=8080

✍️ Task 1: Populate data

It will automatically populate data at startup time.

✍️ Task 2, 3 and 4: Lets visit following endpoints

i. GET: localhost:port/rates/latest
ii. GET: localhost:port/rates/YYYY-MM-DD
iii. GET: localhost:port/rates/analyze

✍️ Bonus

Use only the standard library. It is OK if a library is needed for TDD or database connection.

No library other than the standard library was used. I have used the following 3 external libraries for DB connection and testing.

github.com/golang/mock v1.6.0 (for mocking)
github.com/lib/pq v1.10.6 (for db connection)
github.com/stretchr/testify v1.8.0 (for testing)

📋 Folder Structure

historical-rates
    ├── build
    │   ├── init.sql
    │   ├── docker-compose.yml
    │   ├── Dockerfile
    ├── cmd
    │   ├── app
    │   │   └── main.go                        # application entry point
    │   └── env                                # custom env loader
    │       ├── loader.go
    │       └── loader_test.go
    ├── internal                               # main source directory
    │   └── app
    │       ├── adapter                        # outer layer. all framework, external database and middlewares related code 
    │       │   ├── controller
    │       │   │   ├── rate.go
    │       │   │   ├── rate_test.go
    │       │   │   ├── response_handler.go    # common respponse handler
    │       │   │   ├── response_handler_test.go
    │       │   │   └── server.go
    │       │   ├── db
    │       │   │   └── connections
    │       │   │       └── pg_connection.go   # postgres db connection
    │       │   ├── repository                 # repository implementation
    │       │   │   ├── loader.go
    │       │   │   └── rate.go
    │       │   ├── route.go                  # custom route parser
    │       │   ├── route_test.go
    │       │   └── utils                     # utils functions
    │       │       ├── common.go
    │       │       ├── common_test.go
    │       │       ├── remote.go
    │       │       └── remote_test.go
    │       ├── application                   # middle layer. mainly deals with business logic
    │       │   └── usecase
    │       │       ├── loader.go
    │       │       ├── loader_test.go
    │       │       ├── rate.go
    │       │       └── rate_test.go
    │       └── domain                       # inner layer. all schema and repository defination
    │           ├── rate.go
    │           └── repository
    │               ├── loader.go
    │               ├── mocks                # interface moc for testing
    │               │   ├── loader_mock.go
    │               │   └── rate_mock.go
    │               └── rate.go
    ├── go.mod
    ├── go.sum
    ├── README.md
    └── .env

❓ Challenges and Solution

  • Custom route management
    • As I'm not using any library other than standard library, so routing management was a bit challenging. That's why I have decided to write a custom simple route parser which can solve the purpose of the task. For parsing route I have followed tree mechanism.
  • Load .env
    • There is no inbuilt .env file loader in golang so, I have written a custom .env loader for reading variables from a file and set it to environment.