This is a demo project serving a selection of top festivals in the world.
Festival burning man (Nevada, United States)
Let's say that we already have access to a external API named OpenEventApi (represented by this file, this API does not exist for real). We want to build a wrapper API around this third-party API in order to control the access and add filtering and data transformation features.
- From the API, we want to be able to query a list of events
- filtered them by country, type and ongoing events.
- limited the content to a subset of fields: name, start_date, end_date, type, country.
- To give access to experiences users to this API, we want to create a command-line interface.
The project use the stack: REST endpoint with Node/Express for the API and the CLI.
yarn
yarn is used to build the root and all workspaces at the same time.
npm start
PS: you can use yarn as well
By default the project will run with the env var SAMPLE_MODE
in the server start script in order to test the application manually. In production, this var should be disabled.
Once you have started the server locally, you can run the CLI in an other shell.
There are 2 options
-
You can launch the command line locally
npm run cli
-
OR you can install the package globally in order to use
event-cli
from any directorynpm i -g ./packages/event-cli
- run the automatic tests
npm test
- In a browser: http://localhost:3033
- With Rest Client Extension for VSCode you can use the sample requests file.
- Test the command line
Start the server THEN run one of those commands from any directory:
event-cli
event-cli -h
event-cli list
event-cli list -c USA
event-cli list -c ESP
event-cli list -c spain
The first one is a Question/Answer cli.
Harbin International Ice and Snow Sculpture Festival (Heilongjiang, China)
I've chosen to use a monorepo because it's a clean structure grouping multiple projects in a single version control repository. It allows having clean boundaries like multiple repo but at the same time has many other benefits:
- To get a consistent view of all projects (for better code navigation, refactoring, local tests experience).
- To be able to make an atomic change on multiple packages.
- To reduce the need of configuration to manage versions (Yarn Workspaces do a lot of work to optimize this)
In our company (40 dev/9 teams), the experience of monorepo (more than 100 packages for each part of the site) was really positive.
To generate the boilerplate code of a NodeJS Express server I used express-generator that gives a clean structure (app.js, bin/www, public, /routes). After this setup I've added cross-env (because I'm on windows) and put the port to 3033 (just to not have conflicts with other projects locally).
I've used the following tdd approach:
- Create an empty endpoint and controller. (could have been done afterwards as well)
- Create a test on the controller
- Run the test and let it fail as expected
- Implement the feature in the controller to fix the test.
- Run the test to check it passes. if not come back to the step 4.
- Refactor the code if it's useful and check all tests are still passing.
- Go back to step 2 to cover more use cases (happy path or errors).
I've used Axios for http calls because it's less work, a better syntax and avoids some bugs.
On the server, the global error handling (handleValidationError and handleAllError) takes care of any Error raised in the business code (router, controllers, adapters).
With the addition of wrapAsync
the business code doesn’t have to handle error management in asynchronous code. All errors will bubble up to the global error handlers.
Customs errors (like ConnectivityError) helps to carry some specific edge cases. ValidationError is not used but I keep it in the code to show how I would handle it.
I've used different strategies for tests.
Tests on the controller check only the controller logic in isolation. The adapter to the OpenEventApi is injected in the constructor.
Tests on the route check the full endpoint (route, controller, adapter). The http calls are mocked with the library nock.
sample requests can be used for smoke tests.
The CLI can be installed globally. It runs with commander to parse the command line and inquirer to give a nice interactive UI.
To run the test on each push I've setup the Github action 'Node.js CI'. It's a lightweight solution compared to Teamcity (but much less powerful).
We could have also:
- created an API documentation with Swagger
- used TypeScript instead of Js
- developed a GraphQL server instead or on top of the REST API
- improved code coverage
- created unit tests in the cli