Parse CSV, upload data to DB and serve it with an API. Written in Go, using Postgres for the database.
The application is split into 2 binaries: parser and server. Parser reads the CSV and inserts the data into the DB (only if the table is empty to prevent accidentally erasing modified data). Then, the server makes the data available under a REST API. The app is containerized, and the DB data is persisted in a volume.
- First, create a
.envfile with the required variables. All variable names are listed in.env.example. - Run
docker compose up -dto start the application (I tested with dockerv28.0.4). - Check the
apicontainer's logs to ensure everything is running correctly. - Make HTTP requests to
localhost:8080.
-
First, create a
.env.development.localfile with the required variables. All variable names are listed in.env.example. Note thatDB_HOSTmust be set tolocalhost. -
Install all packages with
go mod tidy. -
Run
docker compose up db -dto start the database. -
You need to run DB migrations. You can do this using any method, for example:
psql "postgres://${DB_USER}:${DB_PASS}@${DB_HOST}:5432/${DB_NAME}?sslmode=disable" -f ./migrations/*.up.sqlmigrate -path migrations -database "postgres://${DB_USER}:${DB_PASS}@${DB_HOST}:5432/${DB_NAME}?sslmode=disable" up(usinggo-migrate)
-
Run
make parseto parse the CSV and to populate the database. -
Run
make serveto start the API server.
I used testcontainers to spin up a unique database for each integration test.
- First, create a
.env.testing.localfile with the required variables. All variable names are listed in.env.example. Note thatDB_HOSTmust be set tolocalhost. - Install all packages with
go mod tidy. - Run
make test.
erDiagram
bank {
VARCHAR(11) swift_code PK
VARCHAR(11) hq_swift_code FK "INDEX"
BOOL is_headquarter "NOT NULL"
TEXT bank_name "NOT NULL"
TEXT address "NOT NULL"
VARCHAR(2) country_iso2_code "NOT NULL | INDEX"
TEXT country_name "NOT NULL"
}
bank 1--0+ bank: "branches"
At first I tried to normalize it as much as possible and move countries to a separate table. But after some time I realized it would just complicate data fetching and slow down the server, so there's only one table.
Additionaly, I thought about removing is_headquarter because if hq_swift_code is NULL then we already know it is a headquarter. However, in the endpoint 3 request structure, isHeadquarter is present so I think it's better to leave it in. Besides, it's easier for a human to check if a bank is the headquarters just by looking at the table contents and seeing an explicit column stating it.